/*
	Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
	и других спецификаций для функционирования компактных приложений на языке
	Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

	Copyright © 2016, 2019 Малик Разработчик

	Это свободная программа: вы можете перераспространять ее и/или изменять
	ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
	в каком она была опубликована Фондом свободного программного обеспечения;
	либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

	Эта программа распространяется в надежде, что она будет полезной,
	но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
	или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
	общественной лицензии GNU.

	Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
	вместе с этой программой. Если это не так, см.
	<https://www.gnu.org/licenses/>.
*/


package com.nokia.mid.ui;

import java.lang.ref.*;
import javax.microedition.lcdui.*;
import malik.emulator.media.graphics.*;

final class DirectGraphicsExtension extends Object
		implements DirectGraphics
{
	private static final int[] GRAPHICS_TO_DIRECT_GRAPHICS_TRANSFORM;
	private static final int[] RASTER_CANVAS_TO_DIRECT_GRAPHICS_TRANSFORM;

	static
	{
		GRAPHICS_TO_DIRECT_GRAPHICS_TRANSFORM = new int[] {
				ROTATE_0, ROTATE_180 | FLIP_HORIZONTAL | FLIP_VERTICAL,
				FLIP_VERTICAL, ROTATE_180 | FLIP_HORIZONTAL,
				FLIP_HORIZONTAL, ROTATE_180 | FLIP_VERTICAL,
				ROTATE_180, FLIP_HORIZONTAL | FLIP_VERTICAL,
				ROTATE_90 | FLIP_VERTICAL, ROTATE_270 | FLIP_HORIZONTAL,
				ROTATE_270, ROTATE_90 | FLIP_HORIZONTAL | FLIP_VERTICAL,
				ROTATE_90, ROTATE_270 | FLIP_HORIZONTAL | FLIP_VERTICAL,
				ROTATE_90 | FLIP_HORIZONTAL, ROTATE_270 | FLIP_VERTICAL
		};
		RASTER_CANVAS_TO_DIRECT_GRAPHICS_TRANSFORM = new int[] {
				ROTATE_0, ROTATE_180 | FLIP_HORIZONTAL | FLIP_VERTICAL,
				ROTATE_270, ROTATE_90 | FLIP_HORIZONTAL | FLIP_VERTICAL,
				ROTATE_180, FLIP_HORIZONTAL | FLIP_VERTICAL,
				ROTATE_90, ROTATE_270 | FLIP_HORIZONTAL | FLIP_VERTICAL,
				FLIP_HORIZONTAL, ROTATE_180 | FLIP_VERTICAL,
				ROTATE_90 | FLIP_HORIZONTAL, ROTATE_270 | FLIP_VERTICAL,
				FLIP_VERTICAL, ROTATE_180 | FLIP_HORIZONTAL,
				ROTATE_90 | FLIP_VERTICAL, ROTATE_270 | FLIP_HORIZONTAL
		};
	}

	private static int toGraphicsTransform(int manipulation)
	{
		int i;
		for(i = GRAPHICS_TO_DIRECT_GRAPHICS_TRANSFORM.length; i-- > 0; )
		{
			if(GRAPHICS_TO_DIRECT_GRAPHICS_TRANSFORM[i] == manipulation)
			{
				return i >> 1;
			}
		}
		return -1;
	}

	private static int toRasterCanvasTransform(int manipulation)
	{
		int i;
		for(i = RASTER_CANVAS_TO_DIRECT_GRAPHICS_TRANSFORM.length; i-- > 0; )
		{
			if(RASTER_CANVAS_TO_DIRECT_GRAPHICS_TRANSFORM[i] == manipulation)
			{
				return i >> 1;
			}
		}
		return -1;
	}


	private WeakReference render;
	private RasterCanvas canvas;

	DirectGraphicsExtension(Graphics render)
	{
		this.render = new WeakReference(render);
		this.canvas = render.getCanvas();
	}

	public void drawImage(Image src, int x, int y, int anchor, int manipulation)
	{
		int srcWidth;
		int srcHeight;
		Graphics render;
		if(src == null)
		{
			throw new NullPointerException("DirectGraphics.drawImage: " +
					"параметр src равен нулевой ссылке.");
		}
		srcWidth = src.getWidth();
		srcHeight = src.getHeight();
		switch(anchor)
		{
		default:
			throw new IllegalArgumentException("DirectGraphics.drawImage: " +
					"недопустимое значение параметра anchor.");
		case 0:
		case Graphics.LEFT | Graphics.TOP:
			break;
		case Graphics.LEFT | Graphics.VCENTER:
			y -= srcHeight / 2;
			break;
		case Graphics.LEFT | Graphics.BOTTOM:
			y -= srcHeight;
			break;
		case Graphics.HCENTER | Graphics.TOP:
			x -= srcWidth / 2;
			break;
		case Graphics.HCENTER | Graphics.VCENTER:
			x -= srcWidth / 2;
			y -= srcHeight / 2;
			break;
		case Graphics.HCENTER | Graphics.BOTTOM:
			x -= srcWidth / 2;
			y -= srcHeight;
			break;
		case Graphics.RIGHT | Graphics.TOP:
			x -= srcWidth;
			break;
		case Graphics.RIGHT | Graphics.VCENTER:
			x -= srcWidth;
			y -= srcHeight / 2;
			break;
		case Graphics.RIGHT | Graphics.BOTTOM:
			x -= srcWidth;
			y -= srcHeight;
			break;
		}
		if((manipulation = toGraphicsTransform(manipulation)) < 0)
		{
			throw new IllegalArgumentException("DirectGraphics.drawImage: " +
					"недопустимое значение параметра manipulation.");
		}
		if((render = (Graphics) this.render.get()) == null)
		{
			return;
		}
		render.drawRegion(src, 0, 0, srcWidth, srcHeight, manipulation, x, y,
				Graphics.LEFT | Graphics.TOP);
	}

	public void drawPixels(byte[] src, byte[] transparencyMask, int offset, int scanlength,
			int left, int top, int width, int height, int manipulation, int format)
	{
		int i;
		int j;
		int x;
		int y;
		int p;
		int t;
		int dj;
		int lim;
		int len;
		int srcOffset;
		int srcLength;
		int dstWidth;
		int dstHeight;
		int translateX;
		int translateY;
		int newOffset;
		int newScanlength;
		int[] pixels;
		Graphics render;
		if(src == null)
		{
			throw new NullPointerException("DirectGraphics.drawPixels: " +
					"параметр src равен нулевой ссылке.");
		}
		if(width < 0 || height < 0)
		{
			throw new IllegalArgumentException("DirectGraphics.drawPixels: " +
					"размеры не могут быть отрицательными.");
		}
		if((manipulation = toRasterCanvasTransform(manipulation)) < 0)
		{
			throw new IllegalArgumentException("DirectGraphics.drawPixels: " +
					"недопустимое значение параметра manipulation.");
		}
		if(scanlength >= 0)
		{
			srcOffset = offset;
			srcLength = scanlength * (height - 1) + width;
		} else
		{
			srcOffset = offset + scanlength * (height - 1);
			srcLength = offset - srcOffset + width;
		}
		if((lim = srcOffset + srcLength) > (len = src.length) ||
				lim < srcOffset || srcOffset > len || srcOffset < 0 || transparencyMask != null &&
				(lim > (len = transparencyMask.length) || srcOffset > len))
		{
			throw new ArrayIndexOutOfBoundsException("DirectGraphics.drawPixels: " +
					"индекс выходит из диапазона.");
		}
		switch(format)
		{
		default:
			throw new IllegalArgumentException("DirectGraphics.drawPixels: " +
					"недопустимое значение параметра format.");
		case TYPE_BYTE_1_GRAY_VERTICAL:
		case TYPE_BYTE_1_GRAY:
		case TYPE_BYTE_2_GRAY:
		case TYPE_BYTE_4_GRAY:
			throw new IllegalArgumentException("DirectGraphics.drawPixels: " +
					"эта реализация не поддерживает форматы пикселов, " +
					"у которых глубина цвета меньше 8 бит на пиксел.");
		case TYPE_BYTE_8_GRAY:
			// p = 000000000000000000000000gray____
			// t = aaaaaaaaaaaaaaaaaaaaaaaaalpha___
			// r = alpha___gray____gray____gray____
			if(width == 0 || height == 0 || (render = (Graphics) this.render.get()) == null)
			{
				return;
			}
			if(scanlength >= -width && scanlength <= width)
			{
				newOffset = offset - srcOffset;
				newScanlength = scanlength;
				pixels = new int[srcLength];
				for(j = srcOffset, i = 0; i < srcLength; j++, i++)
				{
					p = src[j] & 0xff;
					t = transparencyMask == null ? -1 : (int) transparencyMask[j];
					pixels[i] = (t << 24) | (p << 16) | (p << 8) | p;
				}
				break;
			}
			newOffset = 0;
			newScanlength = width;
			pixels = new int[width * height];
			for(i = 0, dj = scanlength - width, j = offset, y = height; y-- > 0; j += dj)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = src[j] & 0xff;
					t = transparencyMask == null ? -1 : (int) transparencyMask[j];
					pixels[i] = (t << 24) | (p << 16) | (p << 8) | p;
				}
			}
			break;
		case TYPE_BYTE_332_RGB:
			// p = rrrrrrrrrrrrrrrrrrrrrrrrr__g__b_
			// t = aaaaaaaaaaaaaaaaaaaaaaaaalpha___
			// r = alpha___r__00000g__00000b_000000
			if(width == 0 || height == 0 || (render = (Graphics) this.render.get()) == null)
			{
				return;
			}
			if(scanlength >= -width && scanlength <= width)
			{
				newOffset = offset - srcOffset;
				newScanlength = scanlength;
				pixels = new int[srcLength];
				for(j = srcOffset, i = 0; i < srcLength; j++, i++)
				{
					p = (int) src[j];
					t = transparencyMask == null ? -1 : (int) transparencyMask[j];
					pixels[i] = (t << 24) |
							((p & 0xe0) << 16) | ((p & 0x1c) << 11) | ((p & 0x03) << 6);
				}
				break;
			}
			newOffset = 0;
			newScanlength = width;
			pixels = new int[width * height];
			for(i = 0, dj = scanlength - width, j = offset, y = height; y-- > 0; j += dj)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = (int) src[j];
					t = transparencyMask == null ? -1 : (int) transparencyMask[j];
					pixels[i] = (t << 24) |
							((p & 0xe0) << 16) | ((p & 0x1c) << 11) | ((p & 0x03) << 6);
				}
			}
			break;
		}
		if((manipulation & 0x01) != 0)
		{
			dstWidth = height;
			dstHeight = width;
		} else
		{
			dstWidth = width;
			dstHeight = height;
		}
		canvas.drawPixelsArea(
				render.getClipX() + (translateX = render.getTranslateX()),
				render.getClipY() + (translateY = render.getTranslateY()),
				render.getClipWidth(), render.getClipHeight(),
				pixels, newOffset, newScanlength, width, height, manipulation,
				left + translateX, top + translateY, dstWidth, dstHeight, true);
	}

	public void drawPixels(short[] src, boolean transparency, int offset, int scanlength,
			int left, int top, int width, int height, int manipulation, int format)
	{
		int i;
		int j;
		int x;
		int y;
		int p;
		int dj;
		int lim;
		int len;
		int srcOffset;
		int srcLength;
		int dstWidth;
		int dstHeight;
		int translateX;
		int translateY;
		int newOffset;
		int newScanlength;
		int[] pixels;
		Graphics render;
		if(src == null)
		{
			throw new NullPointerException("DirectGraphics.drawPixels: " +
					"параметр src равен нулевой ссылке.");
		}
		if(width < 0 || height < 0)
		{
			throw new IllegalArgumentException("DirectGraphics.drawPixels: " +
					"размеры не могут быть отрицательными.");
		}
		if((manipulation = toRasterCanvasTransform(manipulation)) < 0)
		{
			throw new IllegalArgumentException("DirectGraphics.drawPixels: " +
					"недопустимое значение параметра manipulation.");
		}
		if(scanlength >= 0)
		{
			srcOffset = offset;
			srcLength = scanlength * (height - 1) + width;
		} else
		{
			srcOffset = offset + scanlength * (height - 1);
			srcLength = offset - srcOffset + width;
		}
		if((lim = srcOffset + srcLength) > (len = src.length) ||
				lim < srcOffset || srcOffset > len || srcOffset < 0)
		{
			throw new ArrayIndexOutOfBoundsException("DirectGraphics.drawPixels: " +
					"индекс выходит из диапазона.");
		}
		switch(format)
		{
		default:
			throw new IllegalArgumentException("DirectGraphics.drawPixels: " +
					"недопустимое значение параметра format.");
		case TYPE_USHORT_444_RGB:
			// p = ssssssssssssssssstuvr___g___b___
			// r = 11111111r___0000g___0000b___0000
			if(width == 0 || height == 0 || (render = (Graphics) this.render.get()) == null)
			{
				return;
			}
			if(scanlength >= -width && scanlength <= width)
			{
				newOffset = offset - srcOffset;
				newScanlength = scanlength;
				pixels = new int[srcLength];
				for(j = srcOffset, i = 0; i < srcLength; j++, i++)
				{
					p = (int) src[j];
					pixels[i] = 0xff000000 |
							((p & 0x0f00) << 12) | ((p & 0x00f0) << 8) | ((p & 0x000f) << 4);
				}
				break;
			}
			newOffset = 0;
			newScanlength = width;
			pixels = new int[width * height];
			for(i = 0, dj = scanlength - width, j = offset, y = height; y-- > 0; j += dj)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = (int) src[j];
					pixels[i] = 0xff000000 |
							((p & 0x0f00) << 12) | ((p & 0x00f0) << 8) | ((p & 0x000f) << 4);
				}
			}
			break;
		case TYPE_USHORT_555_RGB:
			// p = sssssssssssssssssr____g____b____
			// r = 11111111r____000g____000b____000
			if(width == 0 || height == 0 || (render = (Graphics) this.render.get()) == null)
			{
				return;
			}
			if(scanlength >= -width && scanlength <= width)
			{
				newOffset = offset - srcOffset;
				newScanlength = scanlength;
				pixels = new int[srcLength];
				for(j = srcOffset, i = 0; i < srcLength; j++, i++)
				{
					p = (int) src[j];
					pixels[i] = 0xff000000 |
							((p & 0x7c00) << 9) | ((p & 0x03e0) << 6) | ((p & 0x001f) << 3);
				}
				break;
			}
			newOffset = 0;
			newScanlength = width;
			pixels = new int[width * height];
			for(i = 0, dj = scanlength - width, j = offset, y = height; y-- > 0; j += dj)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = (int) src[j];
					pixels[i] = 0xff000000 |
							((p & 0x7c00) << 9) | ((p & 0x03e0) << 6) | ((p & 0x001f) << 3);
				}
			}
			break;
		case TYPE_USHORT_565_RGB:
			// p = rrrrrrrrrrrrrrrrr____g_____b____
			// r = 11111111r____000g_____00b____000
			if(width == 0 || height == 0 || (render = (Graphics) this.render.get()) == null)
			{
				return;
			}
			if(scanlength >= -width && scanlength <= width)
			{
				newOffset = offset - srcOffset;
				newScanlength = scanlength;
				pixels = new int[srcLength];
				for(j = srcOffset, i = 0; i < srcLength; j++, i++)
				{
					p = (int) src[j];
					pixels[i] = 0xff000000 |
							((p & 0xf800) << 8) | ((p & 0x07e0) << 5) | ((p & 0x001f) << 3);
				}
				break;
			}
			newOffset = 0;
			newScanlength = width;
			pixels = new int[width * height];
			for(i = 0, dj = scanlength - width, j = offset, y = height; y-- > 0; j += dj)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = (int) src[j];
					pixels[i] = 0xff000000 |
							((p & 0xf800) << 8) | ((p & 0x07e0) << 5) | ((p & 0x001f) << 3);
				}
			}
			break;
		case TYPE_USHORT_1555_ARGB:
			// p = aaaaaaaaaaaaaaaaar____g____b____
			// r = aaaaaaaar____000g____000b____000
			if(width == 0 || height == 0 || (render = (Graphics) this.render.get()) == null)
			{
				return;
			}
			if(scanlength >= -width && scanlength <= width)
			{
				newOffset = offset - srcOffset;
				newScanlength = scanlength;
				pixels = new int[srcLength];
				for(j = srcOffset, i = 0; i < srcLength; j++, i++)
				{
					p = (int) src[j];
					pixels[i] = ((p & 0xffff8000) << 9) |
							((p & 0x7c00) << 9) | ((p & 0x03e0) << 6) | ((p & 0x001f) << 3);
				}
				break;
			}
			newOffset = 0;
			newScanlength = width;
			pixels = new int[width * height];
			for(i = 0, dj = scanlength - width, j = offset, y = height; y-- > 0; j += dj)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = (int) src[j];
					pixels[i] = ((p & 0xffff8000) << 9) |
							((p & 0x7c00) << 9) | ((p & 0x03e0) << 6) | ((p & 0x001f) << 3);
				}
			}
			break;
		case TYPE_USHORT_4444_ARGB:
			// p = aaaaaaaaaaaaaaaaa___r___g___b___
			// r = a___a___r___0000g___0000b___0000
			if(width == 0 || height == 0 || (render = (Graphics) this.render.get()) == null)
			{
				return;
			}
			if(scanlength >= -width && scanlength <= width)
			{
				newOffset = offset - srcOffset;
				newScanlength = scanlength;
				pixels = new int[srcLength];
				for(j = srcOffset, i = 0; i < srcLength; j++, i++)
				{
					p = (int) src[j];
					pixels[i] = ((p & 0xf000) << 16) | ((p & 0xf000) << 12) |
							((p & 0x0f00) << 12) | ((p & 0x00f0) << 8) | ((p & 0x000f) << 4);
				}
				break;
			}
			newOffset = 0;
			newScanlength = width;
			pixels = new int[width * height];
			for(i = 0, dj = scanlength - width, j = offset, y = height; y-- > 0; j += dj)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = (int) src[j];
					pixels[i] = ((p & 0xf000) << 16) | ((p & 0xf000) << 12) |
							((p & 0x0f00) << 12) | ((p & 0x00f0) << 8) | ((p & 0x000f) << 4);
				}
			}
			break;
		}
		if((manipulation & 0x01) != 0)
		{
			dstWidth = height;
			dstHeight = width;
		} else
		{
			dstWidth = width;
			dstHeight = height;
		}
		canvas.drawPixelsArea(
				render.getClipX() + (translateX = render.getTranslateX()),
				render.getClipY() + (translateY = render.getTranslateY()),
				render.getClipWidth(), render.getClipHeight(),
				pixels, newOffset, newScanlength, width, height, manipulation,
				left + translateX, top + translateY, dstWidth, dstHeight, transparency);
	}

	public void drawPixels(int[] src, boolean transparency, int offset, int scanlength,
			int left, int top, int width, int height, int manipulation, int format)
	{
		int lim;
		int len;
		int srcOffset;
		int srcLength;
		int dstWidth;
		int dstHeight;
		int translateX;
		int translateY;
		Graphics render;
		if(src == null)
		{
			throw new NullPointerException("DirectGraphics.drawPixels: " +
					"параметр src равен нулевой ссылке.");
		}
		if(width < 0 || height < 0)
		{
			throw new IllegalArgumentException("DirectGraphics.drawPixels: " +
					"размеры не могут быть отрицательными.");
		}
		if((manipulation = toRasterCanvasTransform(manipulation)) < 0)
		{
			throw new IllegalArgumentException("DirectGraphics.drawPixels: " +
					"недопустимое значение параметра manipulation.");
		}
		if(scanlength >= 0)
		{
			srcOffset = offset;
			srcLength = scanlength * (height - 1) + width;
		} else
		{
			srcOffset = offset + scanlength * (height - 1);
			srcLength = offset - srcOffset + width;
		}
		if((lim = srcOffset + srcLength) > (len = src.length) ||
				lim < srcOffset || srcOffset > len || srcOffset < 0)
		{
			throw new ArrayIndexOutOfBoundsException("DirectGraphics.drawPixels: " +
					"индекс выходит из диапазона.");
		}
		switch(format)
		{
		default:
			throw new IllegalArgumentException("DirectGraphics.drawPixels: " +
					"недопустимое значение параметра format.");
		case TYPE_INT_888_RGB:
			transparency = false;
			break;
		case TYPE_INT_8888_ARGB:
			break;
		}
		if(width == 0 || height == 0 || (render = (Graphics) this.render.get()) == null)
		{
			return;
		}
		if((manipulation & 0x01) != 0)
		{
			dstWidth = height;
			dstHeight = width;
		} else
		{
			dstWidth = width;
			dstHeight = height;
		}
		canvas.drawPixelsArea(
				render.getClipX() + (translateX = render.getTranslateX()),
				render.getClipY() + (translateY = render.getTranslateY()),
				render.getClipWidth(), render.getClipHeight(),
				src, offset, scanlength, width, height, manipulation,
				left + translateX, top + translateY, dstWidth, dstHeight, transparency);
	}

	public void drawTriangle(int x1, int y1, int x2, int y2, int x3, int y3, int colorARGB)
	{
		drawLine(x1, y1, x2, y2, colorARGB);
		drawLine(x2, y2, x3, y3, colorARGB);
		drawLine(x3, y3, x1, y1, colorARGB);
	}

	public void drawPolygon(int[] xCoords, int xOffset, int[] yCoords, int yOffset, int nPoints,
			int colorARGB)
	{
		int i;
		int j;
		int k;
		int x0;
		int x1;
		int x2;
		int y0;
		int y1;
		int y2;
		int lim;
		int len;
		if(xCoords == null)
		{
			throw new NullPointerException("DirectGraphics.drawPolygon: " +
					"параметр xCoords равен нулевой ссылке.");
		}
		if(yCoords == null)
		{
			throw new NullPointerException("DirectGraphics.drawPolygon: " +
					"параметр yCoords равен нулевой ссылке.");
		}
		if((lim = xOffset + nPoints) > (len = xCoords.length) ||
				lim < xOffset || xOffset > len || xOffset < 0 ||
				(lim = yOffset + nPoints) > (len = yCoords.length) ||
				lim < yOffset || yOffset > len || yOffset < 0)
		{
			throw new ArrayIndexOutOfBoundsException("DirectGraphics.drawPolygon: " +
					"индекс выходит из диапазона.");
		}
		if(nPoints <= 0)
		{
			return;
		}
		x0 = x2 = xCoords[xOffset];
		y0 = y2 = yCoords[yOffset];
		for(j = yOffset + nPoints - 1, i = xOffset + nPoints - 1,
				k = nPoints; k-- > 0; x2 = x1, y2 = y1, j--, i--)
		{
			x1 = k > 0 ? xCoords[i] : x0;
			y1 = k > 0 ? yCoords[j] : y0;
			drawLine(x1, y1, x2, y2, colorARGB);
		}
	}

	public void fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3, int colorARGB)
	{
		int clipLeft;
		int clipTop;
		int clipWidth;
		int clipHeight;
		int translateX;
		int translateY;
		int tmp;
		int y;
		int ix;
		int idx;
		int idy;
		int ixerr;
		int iyerr;
		int iincx;
		int iincy;
		int id;
		int jx;
		int jdx;
		int jdy;
		int jxerr;
		int jyerr;
		int jincx;
		int jincy;
		int jd;
		Graphics render;
		RasterCanvas canvas;
		if((render = (Graphics) this.render.get()) == null ||
				(clipWidth = render.getClipWidth()) <= 0 ||
				(clipHeight = render.getClipHeight()) <= 0)
		{
			return;
		}
		clipLeft = render.getClipX() + (translateX = render.getTranslateX());
		clipTop = render.getClipY() + (translateY = render.getTranslateY());
		canvas = this.canvas;
		x1 += translateX;
		x2 += translateX;
		x3 += translateX;
		y1 += translateY;
		y2 += translateY;
		y3 += translateY;
		if(y1 > y3)
		{
			tmp = y1;
			y1 = y3;
			y3 = tmp;
			tmp = x1;
			x1 = x3;
			x3 = tmp;
		}
		if(y1 > y2)
		{
			tmp = y1;
			y1 = y2;
			y2 = tmp;
			tmp = x1;
			x1 = x2;
			x2 = tmp;
		}
		if(y2 > y3)
		{
			tmp = y2;
			y2 = y3;
			y3 = tmp;
			tmp = x2;
			x2 = x3;
			x3 = tmp;
		}
		ix = x1;
		ixerr = 0;
		iyerr = 0;
		iincx = (idx = x2 - x1) == 0 ? 0 : (idx < 0 ? -1 : 1);
		iincy = (idy = y2 - y1) == 0 ? 0 : (idy < 0 ? -1 : 1);
		id = (idx = idx < 0 ? -idx : idx) >= (idy = idy < 0 ? -idy : idy) ? idx : idy;
		jx = x1;
		jxerr = 0;
		jyerr = 0;
		jincx = (jdx = x3 - x1) == 0 ? 0 : (jdx < 0 ? -1 : 1);
		jincy = (jdy = y3 - y1) == 0 ? 0 : (jdy < 0 ? -1 : 1);
		jd = (jdx = jdx < 0 ? -jdx : jdx) >= (jdy = jdy < 0 ? -jdy : jdy) ? jdx : jdy;
		for(y = y1; y <= y3; y++)
		{
			if(y == y2)
			{
				ix = x2;
				ixerr = 0;
				iyerr = 0;
				iincx = (idx = x3 - x2) == 0 ? 0 : (idx < 0 ? -1 : 1);
				iincy = (idy = y3 - y2) == 0 ? 0 : (idy < 0 ? -1 : 1);
				id = (idx = idx < 0 ? -idx : idx) >= (idy = idy < 0 ? -idy : idy) ? idx : idy;
			}
			if(ix <= jx)
			{
				canvas.drawHorzLine(clipLeft, clipTop, clipWidth, clipHeight,
						ix, y, jx - ix + 1, colorARGB, true);
			} else
			{
				canvas.drawHorzLine(clipLeft, clipTop, clipWidth, clipHeight,
						jx, y, ix - jx + 1, colorARGB, true);
			}
			while(iincy > 0)
			{
				ixerr += idx;
				iyerr += idy;
				if(ixerr > id)
				{
					ixerr -= id;
					ix += iincx;
				}
				if(iyerr > id)
				{
					iyerr -= id;
					break;
				}
			}
			while(jincy > 0)
			{
				jxerr += jdx;
				jyerr += jdy;
				if(jxerr > jd)
				{
					jxerr -= jd;
					jx += jincx;
				}
				if(jyerr > jd)
				{
					jyerr -= jd;
					break;
				}
			}
		}
	}

	public void fillPolygon(int[] xCoords, int xOffset, int[] yCoords, int yOffset, int nPoints,
			int colorARGB)
	{
		int clipLeft;
		int clipTop;
		int clipWidth;
		int clipHeight;
		int translateX;
		int translateY;
		int lim;
		int len;
		int x1;
		int x2;
		int y1;
		int y2;
		int i;
		int j;
		int x;
		int y;
		int ymin;
		int ymax;
		int ixnm;
		int[] px;
		int[] py;
		int[] ix;
		Graphics render;
		RasterCanvas canvas;
		if(xCoords == null)
		{
			throw new NullPointerException("DirectGraphics.fillPolygon: " +
					"параметр xCoords равен нулевой ссылке.");
		}
		if(yCoords == null)
		{
			throw new NullPointerException("DirectGraphics.fillPolygon: " +
					"параметр yCoords равен нулевой ссылке.");
		}
		if((lim = xOffset + nPoints) > (len = xCoords.length) ||
				lim < xOffset || xOffset > len || xOffset < 0 ||
				(lim = yOffset + nPoints) > (len = yCoords.length) ||
				lim < yOffset || yOffset > len || yOffset < 0)
		{
			throw new ArrayIndexOutOfBoundsException("DirectGraphics.fillPolygon: " +
					"индекс выходит из диапазона.");
		}
		if(nPoints <= 2 ||
				(render = (Graphics) this.render.get()) == null ||
				(clipWidth = render.getClipWidth()) <= 0 ||
				(clipHeight = render.getClipHeight()) <= 0)
		{
			return;
		}
		clipLeft = render.getClipX() + (translateX = render.getTranslateX());
		clipTop = render.getClipY() + (translateY = render.getTranslateY());
		canvas = this.canvas;
		px = new int[nPoints + 1];
		py = new int[nPoints + 1];
		ix = new int[nPoints & (-0x02)];
		Array.copy(xCoords, xOffset, px, 0, nPoints);
		Array.copy(yCoords, yOffset, py, 0, nPoints);
		px[nPoints] = px[0];
		py[nPoints] = py[0];
		for(ymin = ymax = py[0], i = nPoints; i-- > 0; )
		{
			if(ymin > (y = py[i]))
			{
				ymin = y;
			}
			if(ymax < y)
			{
				ymax = y;
			}
		}
		for(y = ymin; y < ymax; y++)
		{
			for(ixnm = 0, x1 = px[nPoints], y1 = py[nPoints], i = nPoints; i-- > 0; )
			{
				x2 = x1;
				y2 = y1;
				x1 = px[i];
				y1 = py[i];
				if(y < (y1 <= y2 ? y1 : y2) || y >= (y1 >= y2 ? y1 : y2))
				{
					continue;
				}
				x = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
				switch(ixnm)
				{
				default:
					if(x > ix[ixnm - 1])
					{
						ix[ixnm++] = x;
						break;
					}
					for(j = 0; j < ixnm; j++)
					{
						if(x < ix[j])
						{
							Array.copy(ix, j, ix, j + 1, ixnm++ - j);
							ix[j] = x;
							break;
						}
					}
					break;
				case 1:
					if(x < ix[0])
					{
						ix[ixnm++] = ix[0];
						ix[0] = x;
						break;
					}
					/* fall through */
				case 0:
					ix[ixnm++] = x;
					break;
				}
			}
			for(j = ixnm; (j -= 2) >= 0; )
			{
				if((x1 = ix[j]) < (x2 = ix[j + 1]))
				{
					canvas.drawHorzLine(clipLeft, clipTop, clipWidth, clipHeight,
							x1 + translateX, y + translateY, x2 - x1, colorARGB, true);
				}
			}
		}
	}

	public void setARGBColor(int colorARGB)
	{
		Graphics render;
		if((render = (Graphics) this.render.get()) != null)
		{
			render.setColor(colorARGB);
		}
	}

	public void getPixels(byte[] dst, byte[] transparencyMask, int offset, int scanlength,
			int left, int top, int width, int height, int format)
	{
		boolean opaque;
		int x;
		int y;
		int p;
		int i;
		int di;
		int j;
		int dj;
		int lim;
		int len;
		int dstOffset;
		int dstLength;
		int srcOffset;
		int srcScanlength;
		int[] src;
		Graphics render;
		RasterCanvas canvas;
		GraphicBuffer buffer;
		if(dst == null)
		{
			throw new NullPointerException("DirectGraphics.getPixels: " +
					"параметр dst равен нулевой ссылке.");
		}
		if(scanlength > -width && scanlength < width)
		{
			throw new IllegalArgumentException("DirectGraphics.getPixels: " +
					"длина линии сканирования не может быть меньше ширины.");
		}
		if(scanlength >= 0)
		{
			dstOffset = offset;
			dstLength = scanlength * (height - 1) + width;
		} else
		{
			dstOffset = offset + scanlength * (height - 1);
			dstLength = offset - dstOffset + width;
		}
		if((lim = dstOffset + dstLength) > (len = dst.length) ||
				lim < dstOffset || dstOffset > len || dstOffset < 0 || transparencyMask != null &&
				(lim > (len = transparencyMask.length) || dstOffset > len))
		{
			throw new ArrayIndexOutOfBoundsException("DirectGraphics.getPixels: " +
					"индекс выходит из диапазона.");
		}
		if(width < 0 || height < 0)
		{
			throw new IllegalArgumentException("DirectGraphics.getPixels: " +
					"размеры не могут быть отрицательными.");
		}
		if((render = (Graphics) this.render.get()) != null)
		{
			left += render.getTranslateX();
			top += render.getTranslateY();
		}
		buffer = (canvas = this.canvas).getBuffer();
		if((lim = left + width) > (len = buffer.getWidth()) ||
				lim < left || left > len || left < 0 ||
				(lim = top + height) > (len = buffer.getHeight()) ||
				lim < top || top > len || top < 0)
		{
			throw new IllegalArgumentException("DirectGraphics.getPixels: " +
					"заданная прямоугольная область выходит за пределы растровой канвы.");
		}
		if(width == 0 || height == 0)
		{
			return;
		}
		opaque = canvas.isScreenCanvas();
		src = buffer.getPixels();
		srcOffset = buffer.getOffset();
		srcScanlength = buffer.getScanlength();
		switch(format)
		{
		default:
			throw new IllegalArgumentException("DirectGraphics.getPixels: " +
					"недопустимое значение параметра format.");
		case TYPE_BYTE_1_GRAY_VERTICAL:
		case TYPE_BYTE_1_GRAY:
		case TYPE_BYTE_2_GRAY:
		case TYPE_BYTE_4_GRAY:
			throw new IllegalArgumentException("DirectGraphics.getPixels: " +
					"эта реализация не поддерживает форматы пикселов, " +
					"у которых глубина цвета меньше 8 бит на пиксел.");
		case TYPE_BYTE_8_GRAY:
			for(dj = srcScanlength - width, j = srcOffset + srcScanlength * top + left,
					di = scanlength - width, i = offset, y = height; y-- > 0; j += dj, i += di)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = src[j];
					dst[i] = (byte) ((76 * ((p >> 16) & 0xff) + 152 * ((p >> 8) & 0xff) +
							28 * (p & 0xff)) >> 8);
					if(transparencyMask != null)
					{
						transparencyMask[i] = opaque ? ((byte) 0xff) : ((byte) (p >> 24));
					}
				}
			}
			break;
		case TYPE_BYTE_332_RGB:
			// p = alpha___r__xxxxxg__xxxxxb_xxxxxx
			// r =                         r__g__b_
			for(dj = srcScanlength - width, j = srcOffset + srcScanlength * top + left,
					di = scanlength - width, i = offset, y = height; y-- > 0; j += dj, i += di)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = src[j];
					dst[i] = (byte) (((p & 0x00e00000) >> 16) | ((p & 0x0000e000) >> 11) |
							((p & 0x000000c0) >> 6));
					if(transparencyMask != null)
					{
						transparencyMask[i] = opaque ? ((byte) 0xff) : ((byte) (p >> 24));
					}
				}
			}
			break;
		}
	}

	public void getPixels(short[] dst, int offset, int scanlength,
			int left, int top, int width, int height, int format)
	{
		boolean opaque;
		int x;
		int y;
		int p;
		int i;
		int di;
		int j;
		int dj;
		int lim;
		int len;
		int dstOffset;
		int dstLength;
		int srcOffset;
		int srcScanlength;
		int[] src;
		Graphics render;
		RasterCanvas canvas;
		GraphicBuffer buffer;
		if(dst == null)
		{
			throw new NullPointerException("DirectGraphics.getPixels: " +
					"параметр dst равен нулевой ссылке.");
		}
		if(scanlength > -width && scanlength < width)
		{
			throw new IllegalArgumentException("DirectGraphics.getPixels: " +
					"длина линии сканирования не может быть меньше ширины.");
		}
		if(scanlength >= 0)
		{
			dstOffset = offset;
			dstLength = scanlength * (height - 1) + width;
		} else
		{
			dstOffset = offset + scanlength * (height - 1);
			dstLength = offset - dstOffset + width;
		}
		if((lim = dstOffset + dstLength) > (len = dst.length) ||
				lim < dstOffset || dstOffset > len || dstOffset < 0)
		{
			throw new ArrayIndexOutOfBoundsException("DirectGraphics.getPixels: " +
					"индекс выходит из диапазона.");
		}
		if(width < 0 || height < 0)
		{
			throw new IllegalArgumentException("DirectGraphics.getPixels: " +
					"размеры не могут быть отрицательными.");
		}
		if((render = (Graphics) this.render.get()) != null)
		{
			left += render.getTranslateX();
			top += render.getTranslateY();
		}
		buffer = (canvas = this.canvas).getBuffer();
		if((lim = left + width) > (len = buffer.getWidth()) ||
				lim < left || left > len || left < 0 ||
				(lim = top + height) > (len = buffer.getHeight()) ||
				lim < top || top > len || top < 0)
		{
			throw new IllegalArgumentException("DirectGraphics.getPixels: " +
					"заданная прямоугольная область выходит за пределы растровой канвы.");
		}
		if(width == 0 || height == 0)
		{
			return;
		}
		opaque = canvas.isScreenCanvas();
		src = buffer.getPixels();
		srcOffset = buffer.getOffset();
		srcScanlength = buffer.getScanlength();
		switch(format)
		{
		default:
			throw new IllegalArgumentException("DirectGraphics.getPixels: " +
					"недопустимое значение параметра format.");
		case TYPE_USHORT_444_RGB:
			// p = alpha___r___xxxxg___xxxxb___xxxx
			// r =                 0000r___g___b___
			for(dj = srcScanlength - width, j = srcOffset + srcScanlength * top + left,
					di = scanlength - width, i = offset, y = height; y-- > 0; j += dj, i += di)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = src[j];
					dst[i] = (short) (((p & 0x00f00000) >> 12) | ((p & 0x0000f000) >> 8) |
							((p & 0x000000f0) >> 4));
				}
			}
			break;
		case TYPE_USHORT_555_RGB:
			// p = alpha___r____xxxg____xxxb____xxx
			// r =                 0r____g____b____
			for(dj = srcScanlength - width, j = srcOffset + srcScanlength * top + left,
					di = scanlength - width, i = offset, y = height; y-- > 0; j += dj, i += di)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = src[j];
					dst[i] = (short) (((p & 0x00f80000) >> 9) | ((p & 0x0000f800) >> 6) |
							((p & 0x000000f8) >> 3));
				}
			}
			break;
		case TYPE_USHORT_565_RGB:
			// p = alpha___r____xxxg_____xxb____xxx
			// r =                 r____g_____b____
			for(dj = srcScanlength - width, j = srcOffset + srcScanlength * top + left,
					di = scanlength - width, i = offset, y = height; y-- > 0; j += dj, i += di)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = src[j];
					dst[i] = (short) (((p & 0x00f80000) >> 8) | ((p & 0x0000fc00) >> 5) |
							((p & 0x000000f8) >> 3));
				}
			}
			break;
		case TYPE_USHORT_1555_ARGB:
			// p = alpha___r____xxxg____xxxb____xxx
			// r =                 ar____g____b____
			for(dj = srcScanlength - width, j = srcOffset + srcScanlength * top + left,
					di = scanlength - width, i = offset, y = height; y-- > 0; j += dj, i += di)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = src[j];
					dst[i] = (short) ((opaque || (p >>> 24) >= 0x80 ? 0x8000 : 0x0000) |
							((p & 0x00f80000) >> 9) | ((p & 0x0000f800) >> 6) |
							((p & 0x000000f8) >> 3));
				}
			}
			break;
		case TYPE_USHORT_4444_ARGB:
			// p = alpha___r___xxxxg___xxxxb___xxxx
			// r =                 alphr___g___b___
			for(dj = srcScanlength - width, j = srcOffset + srcScanlength * top + left,
					di = scanlength - width, i = offset, y = height; y-- > 0; j += dj, i += di)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = src[j];
					dst[i] = (short) ((opaque ? 0xf000 : (p & 0xf0000000) >> 16) |
							((p & 0x00f00000) >> 12) | ((p & 0x0000f000) >> 8) |
							((p & 0x000000f0) >> 4));
				}
			}
			break;
		}
	}

	public void getPixels(int[] dst, int offset, int scanlength,
			int left, int top, int width, int height, int format)
	{
		boolean opaque;
		int x;
		int y;
		int p;
		int i;
		int di;
		int j;
		int dj;
		int lim;
		int len;
		int dstOffset;
		int dstLength;
		int srcOffset;
		int srcScanlength;
		int[] src;
		Graphics render;
		RasterCanvas canvas;
		GraphicBuffer buffer;
		if(dst == null)
		{
			throw new NullPointerException("DirectGraphics.getPixels: " +
					"параметр dst равен нулевой ссылке.");
		}
		if(scanlength > -width && scanlength < width)
		{
			throw new IllegalArgumentException("DirectGraphics.getPixels: " +
					"длина линии сканирования не может быть меньше ширины.");
		}
		if(scanlength >= 0)
		{
			dstOffset = offset;
			dstLength = scanlength * (height - 1) + width;
		} else
		{
			dstOffset = offset + scanlength * (height - 1);
			dstLength = offset - dstOffset + width;
		}
		if((lim = dstOffset + dstLength) > (len = dst.length) ||
				lim < dstOffset || dstOffset > len || dstOffset < 0)
		{
			throw new ArrayIndexOutOfBoundsException("DirectGraphics.getPixels: " +
					"индекс выходит из диапазона.");
		}
		if(width < 0 || height < 0)
		{
			throw new IllegalArgumentException("DirectGraphics.getPixels: " +
					"размеры не могут быть отрицательными.");
		}
		if((render = (Graphics) this.render.get()) != null)
		{
			left += render.getTranslateX();
			top += render.getTranslateY();
		}
		buffer = (canvas = this.canvas).getBuffer();
		if((lim = left + width) > (len = buffer.getWidth()) ||
				lim < left || left > len || left < 0 ||
				(lim = top + height) > (len = buffer.getHeight()) ||
				lim < top || top > len || top < 0)
		{
			throw new IllegalArgumentException("DirectGraphics.getPixels: " +
					"заданная прямоугольная область выходит за пределы растровой канвы.");
		}
		if(width == 0 || height == 0)
		{
			return;
		}
		opaque = canvas.isScreenCanvas();
		src = buffer.getPixels();
		srcOffset = buffer.getOffset();
		srcScanlength = buffer.getScanlength();
		switch(format)
		{
		default:
			throw new IllegalArgumentException("DirectGraphics.getPixels: " +
					"недопустимое значение параметра format.");
		case TYPE_INT_888_RGB:
			// p = alpha___r_______g_______b_______
			// r = 00000000r_______g_______b_______
			for(dj = srcScanlength - width, j = srcOffset + srcScanlength * top + left,
					di = scanlength - width, i = offset, y = height; y-- > 0; j += dj, i += di)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = src[j];
					dst[i] = p & 0x00ffffff;
				}
			}
			break;
		case TYPE_INT_8888_ARGB:
			// p = alpha___r_______g_______b_______
			// r = alpha___r_______g_______b_______
			for(dj = srcScanlength - width, j = srcOffset + srcScanlength * top + left,
					di = scanlength - width, i = offset, y = height; y-- > 0; j += dj, i += di)
			{
				for(x = width; x-- > 0; j++, i++)
				{
					p = src[j];
					dst[i] = opaque ? p | 0xff000000 : p;
				}
			}
			break;
		}
	}

	public int getNativePixelFormat()
	{
		return canvas.isScreenCanvas() ? TYPE_INT_888_RGB : TYPE_INT_8888_ARGB;
	}

	public int getAlphaComponent()
	{
		return 0xff;
	}

	private void drawLine(int x1, int y1, int x2, int y2, int color)
	{
		int clipLeft;
		int clipRight;
		int clipTop;
		int clipBottom;
		int clipWidth;
		int clipHeight;
		int translateX;
		int translateY;
		int tmp;
		int xerr;
		int yerr;
		int dx;
		int dy;
		int x;
		int y;
		int incx;
		int incy;
		int d;
		int i;
		int ofs;
		int scan;
		int[] pixels;
		Graphics render;
		GraphicBuffer buffer;
		RasterCanvas canvas;
		if((render = (Graphics) this.render.get()) == null)
		{
			return;
		}
		canvas = this.canvas;
		clipLeft = Math.max(tmp = render.getClipX() + (translateX = render.getTranslateX()), 0);
		clipRight = Math.min(tmp + render.getClipWidth(), canvas.getWidth());
		clipTop = Math.max(tmp = render.getClipY() + (translateY = render.getTranslateY()), 0);
		clipBottom = Math.min(tmp + render.getClipHeight(), canvas.getHeight());
		if((clipWidth = clipRight - clipLeft) <= 0 || (clipHeight = clipBottom - clipTop) <= 0)
		{
			return;
		}
		color = (color & 0x00ffffff) | (canvas.isScreenCanvas() ? 0 : 0xff000000);
		x1 += translateX;
		x2 += translateX;
		y1 += translateY;
		y2 += translateY;
		if(x1 == x2)
		{
			if(y1 <= y2)
			{
				canvas.drawVertLine(clipLeft, clipTop, clipWidth, clipHeight,
						x1, y1, y2 - y1 + 1, color, false);
				return;
			} else
			{
				canvas.drawVertLine(clipLeft, clipTop, clipWidth, clipHeight,
						x2, y2, y1 - y2 + 1, color, false);
				return;
			}
		}
		if(y1 == y2)
		{
			if(x1 <= x2)
			{
				canvas.drawHorzLine(clipLeft, clipTop, clipWidth, clipHeight,
						x1, y1, x2 - x1 + 1, color, false);
				return;
			} else
			{
				canvas.drawHorzLine(clipLeft, clipTop, clipWidth, clipHeight,
						x2, y2, x1 - x2 + 1, color, false);
				return;
			}
		}
		x = x1;
		y = y1;
		xerr = 0;
		yerr = 0;
		incx = (dx = x2 - x1) == 0 ? 0 : (dx < 0 ? -1 : 1);
		incy = (dy = y2 - y1) == 0 ? 0 : (dy < 0 ? -1 : 1);
		d = (dx = dx < 0 ? -dx : dx) >= (dy = dy < 0 ? -dy : dy) ? dx : dy;
		ofs = (buffer = canvas.getBuffer()).getOffset();
		scan = buffer.getScanlength();
		pixels = buffer.getPixels();
		for(i = 0; i <= d; i++)
		{
			xerr += dx;
			yerr += dy;
			if(xerr > d)
			{
				xerr -= d;
				x += incx;
			}
			if(yerr > d)
			{
				yerr -= d;
				y += incy;
			}
			if(x >= clipLeft && x < clipRight && y >= clipTop && y < clipBottom)
			{
				pixels[ofs + x + y * scan] = color;
			}
		}
	}
}
