#help_index "Graphics"

Option(OPTf_WARN_HEADER_MISMATCH, OFF);
public Bool GrPlot0(CDC *dc=gr.dc, I64 x, I64 y)
{//2D. No clipping or transformation or thick.
	U8				*dst;
	I32				*db;
	I64				 d, dist;
	CColorROPU32	 c, color = dc->color, bkcolor = dc->bkcolor;
	Bool			 was_timer_rand = Bts(&Fs->task_flags, TASKf_NONTIMER_RAND);

	if (dc->flags & DCF_LOCATE_NEAREST)
	{
		dist = DistSqrI64(x, y, dc->cur_x, dc->cur_y);
		if (dist <= dc->nearest_dist)
			dc->nearest_dist = dist;
	}
	if (dc->flags & DCF_RECORD_EXTENTS)
	{
		if (x < dc->min_x)
			dc->min_x = x;

		if (x > dc->max_x)
			dc->max_x = x;

		if (y < dc->min_y)
			dc->min_y = y;

		if (y > dc->max_y)
			dc->max_y = y;
	}
	if (dc->flags & DCF_DONT_DRAW)
		return TRUE;
	d = dc->width_internal * y + x;
	if (db = dc->depth_buf)
	{
		db += d;
		if (0 <= dc->db_z <= *db)
			*db = dc->db_z;
		else
			return TRUE;
	}
	if (color.c1.rop & (ROPBF_DITHER | ROPBF_PROBABILITY_DITHER))
	{
		if (color.c1.rop & ROPBF_PROBABILITY_DITHER)
		{
			if (RandU16<dc->dither_probability_u16)
			{
				color.c1.rop = color.c0.rop;
				color.c0	 = color.c1;
			}
			BEqual(&Fs->task_flags, TASKf_NONTIMER_RAND, was_timer_rand);
		}
		else
		{
			if ((x ^ y) & 1)
			{
				color.c1.rop = color.c0.rop;
				color.c0	 = color.c1;
			}
		}
	}
	dst = dc->body + d;
	switch [color.c0.rop]
	{
		case ROPB_EQU:
		case ROPB_MONO:
			*dst = color.c0.color;
			break;

		case ROPB_COLLISION:
			c = *dst;
			if (c != TRANSPARENT && c != bkcolor.c0.color)
				dc->collision_count++;
			break;

		case ROPB_XOR:
			*dst ^= color.c0.color;
			break;
	}

	return TRUE;
}
Option(OPTf_WARN_HEADER_MISMATCH, ON);

public I64 GrPeek0(CDC *dc=gr.dc, I64 x, I64 y)
{//2D. No clipping or transformation.
	return dc->body[dc->width_internal * y + x];
}

#help_index "Graphics;Graphics/Device Contexts"

public I64 GrBlot(CDC *dc=gr.dc, I64 x, I64 y, CDC *img)
{//2D. Clipping but not transformation..
	I64				 i, j, k, k1, kk, kk1, w1, h1, w2, h2, dist, 
					 leading_pixels, leading_pixel_mask, whole_I64s, 
					 trailing_pixels, trailing_pixel_mask, 
				reg  bit_shift, win_z_buf_line_inc, win_z_buf_line_dec, win_z_num, 
					 color_mask;
	U8			reg *dst, *src;
	I32				*db;
	U16			reg *win_z_buf_ptr;
	CColorROPU32	 color, c, old_color;
	CTask			*win_task;

	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		win_task = dc->win_task;
		x += win_task->scroll_x;
		y += win_task->scroll_y;
	}
	if (x < 0)
		w1 = -x;
	else
		w1 = 0;
	if (y < 0)
		h1 = -y;
	else
		h1 = 0;
	w2 = img->width;
	h2 = img->height;
	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		x += win_task->pix_left;
		y += win_task->pix_top;
	}
	if (dc->flags & DCF_LOCATE_NEAREST)
	{
		dist = DistSqrI64(x + img->width >> 1, y + img->height >> 1, dc->cur_x, dc->cur_y);
		if (dist <= dc->nearest_dist)
			dc->nearest_dist = dist;
	}
	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		if (x + w1 < 0)
			w1=-x;
		if (x + w2 > win_task->pix_right + 1)
			w2 = win_task->pix_right + 1 - x;

		if (y + h1 < 0)
			h1 = -y;
		if (y + h2 > win_task->pix_bottom + 1)
			h2 = win_task->pix_bottom + 1 - y;
	}
	if (x + w2>dc->width)
		w2 = dc->width - x;
	if (y + h2 > dc->height)
		h2 = dc->height - y;
	if (w1 < w2 <= img->width && h1 < h2 <= img->height)
	{
		if (dc->flags & DCF_RECORD_EXTENTS)
		{
			if (x + w1 < dc->min_x)
				dc->min_x = x + w1;

			if (x + w2 - 1 > dc->max_x)
				dc->max_x = x + w2 - 1;

			if (y + h1 < dc->min_y)
				dc->min_y = y + h1;

			if (y + h2 - 1 > dc->max_y)
				dc->max_y = y + h2 - 1;
		}
		if (dc->flags & DCF_DONT_DRAW)
			return 1;

		old_color = dc->color;
		db = dc->depth_buf;
		dc->depth_buf = NULL;
		dc->color &= ~ROPF_DITHER;
		color = dc->color;
		leading_pixels = -(w1 + x) & 7;
		leading_pixel_mask = gr.to_8_bits[0xFF >> leading_pixels];
		bit_shift = -x & 7;
		whole_I64s = (w2 - w1 - leading_pixels) >> 3;
		if (whole_I64s < 0)
			whole_I64s = 0;
		trailing_pixels = (x + w2) & 7;
		trailing_pixel_mask = gr.to_8_bits[0xFF << trailing_pixels & 0xFF];
		if (leading_pixels + trailing_pixels > w2 - w1)
		{
			leading_pixel_mask |= trailing_pixel_mask;
			trailing_pixels = 0;
		}
		switch (color.c0.rop)
		{
			case ROPB_COLLISION: //TODO: Might want to check win_z_buf
				color = dc->bkcolor.c0.color;
				k = h1 * img->width_internal;
				k1 = (h1 + y) * dc->width_internal + x;
				for (j = h2 - h1; j; j--)
				{
					for (i = w1; i < w2; i++)
					{
						c = dc->body[k1 + i];
						if (c != TRANSPARENT && c != color && img->body[k + i] != TRANSPARENT)
							dc->collision_count++;
					}
					k += img->width_internal;
					k1 += dc->width_internal;
				}
				break;

			case ROPB_MONO:
				color_mask = gr.to_8_colors[color.c0.color];
				if (img->flags & DCF_NO_TRANSPARENTS)
				{
					if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags & DCF_ON_TOP)
						win_z_buf_ptr = NULL;
					else
					{
						win_z_num = win_task->win_z_num;
						win_z_buf_ptr = gr.win_z_buf(U8 *) +
										((h1 + y) / FONT_HEIGHT * TEXT_COLS + (w1 + x) / FONT_WIDTH) * sizeof(U16);
						win_z_buf_line_dec = whole_I64s;
						if (leading_pixels)
							win_z_buf_line_dec++;
						if (trailing_pixels)
							win_z_buf_line_dec++;
						win_z_buf_line_dec *= sizeof(U16);
						win_z_buf_line_inc = TEXT_COLS * sizeof(U16) - win_z_buf_line_dec;
					}
					kk = h1 * img->width_internal + w1;
					kk1 = (h1 + y) * dc->width_internal + x + w1;
					kk = (kk - bit_shift) & ~7 + bit_shift;
					bit_shift *= 8;
					if (win_z_buf_ptr)
						for (j = h1; j < h2; j++)
						{
							src = img->body + kk  & ~7;
							dst = dc->body  + kk1 & ~7;
							if (leading_pixels)
							{
								if (win_z_num >= *win_z_buf_ptr++)
								{
									if (bit_shift)
										*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask |
													   (*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift)) &
														~leading_pixel_mask & color_mask;
									else
										*dst(I64 *)++ = *dst(I64 *)   &  leading_pixel_mask |
														*src(I64 *)++ & ~leading_pixel_mask & color_mask;
								}
								else
								{
									src(I64 *)++;
									dst(I64 *)++;
								}
							}
							if (bit_shift)
								for (i = 0; i < whole_I64s; i++)
									if (win_z_num >= *win_z_buf_ptr++)
										*dst(I64 *)++ = (*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift)) &
														color_mask;
									else
									{
										src(I64 *)++;
										dst(I64 *)++;
									}
							else
								for (i = 0; i < whole_I64s; i++)
									if (win_z_num >= *win_z_buf_ptr++)
										*dst(I64 *)++ = *src(I64 *)++ & color_mask;
									else
									{
										src(I64 *)++;
										dst(I64 *)++;
									}
							if (trailing_pixels && win_z_num >= *win_z_buf_ptr++)
							{
								if (bit_shift)
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
												 (*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift)) &
												  ~trailing_pixel_mask & color_mask;
								else
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
												  *src(I64 *)++ & ~trailing_pixel_mask & color_mask;
							}
							kk  += img->width_internal;
							kk1 += dc->width_internal;
							if ((j + y) & 7 == 7)
								win_z_buf_ptr(U8 *) += win_z_buf_line_inc;
							else
								win_z_buf_ptr(U8 *) -= win_z_buf_line_dec;
						}
					else
						for (j = h2 - h1; j; j--)
						{
							src = img->body + kk  & ~7;
							dst = dc->body  + kk1 & ~7;
							if (leading_pixels)
							{
								if (bit_shift)
									*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask |
												   (*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift)) &
													~leading_pixel_mask & color_mask;
								else
									*dst(I64 *)++ = *dst(I64 *)   &  leading_pixel_mask |
													*src(I64 *)++ & ~leading_pixel_mask & color_mask;
							}
							if (bit_shift)
								for (i = 0; i < whole_I64s; i++)
									*dst(I64 *)++ = (*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift)) &
													color_mask;
							else
								for (i = 0; i < whole_I64s; i++)
									*dst(I64 *)++ = *src(I64 *)++ & color_mask;

							if (trailing_pixels)
							{
								if (bit_shift)
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
												 (*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift)) &
												  ~trailing_pixel_mask & color_mask;
								else
									*dst(I64 *) = *dst(I64 *)   &  trailing_pixel_mask |
												  *src(I64 *)++ & ~trailing_pixel_mask & color_mask;
							}
							kk  += img->width_internal;
							kk1 += dc->width_internal;
						}
				}
				else
				{
					k = h1 * img->width_internal;
					if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags & DCF_ON_TOP)
					{
						for (j = h1; j < h2; j++)
						{
							for (i = w1; i < w2; i++)
								if (img->body[k + i])
									GrPlot0(dc, x + i, y + j);
							k += img->width_internal;
						}
					}
					else
					{
						win_z_num = win_task->win_z_num;
						win_z_buf_ptr = gr.win_z_buf(U8 *) +
										((h1 + y) / FONT_HEIGHT * TEXT_COLS + (w1 + x) / FONT_WIDTH) * sizeof(U16);
						win_z_buf_line_dec = whole_I64s;
						if (leading_pixels)
							win_z_buf_line_dec++;
						if (trailing_pixels)
							win_z_buf_line_dec++;
						win_z_buf_line_dec *= sizeof(U16);
						win_z_buf_line_inc = TEXT_COLS * sizeof(U16) - win_z_buf_line_dec;
						for (j = h1; j < h2; j++)
						{
							if (win_z_num >= *win_z_buf_ptr++)
								color_mask = TRUE;
							else
								color_mask = FALSE;
							for (i = w1; i < w2;)
							{
								if (color_mask)
									if (img->body[k + i])
										GrPlot0(dc, x + i, y + j);
								if (!((++i + x) & 7) && i < w2)
								{
									if (win_z_num >= *win_z_buf_ptr++)
										color_mask = TRUE;
									else
										color_mask = FALSE;
								}
							}
							if ((j + y) & 7 == 7)
								win_z_buf_ptr(U8 *) += win_z_buf_line_inc;
							else
								win_z_buf_ptr(U8 *) -= win_z_buf_line_dec;
							k += img->width_internal;
						}
					}
				}
				break;

			case ROPB_EQU:
				if (img->flags & DCF_NO_TRANSPARENTS)
				{
					if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags & DCF_ON_TOP)
						win_z_buf_ptr = NULL;
					else
					{
						win_z_num = win_task->win_z_num;
						win_z_buf_ptr = gr.win_z_buf(U8 *) +
										((h1 + y) / FONT_HEIGHT * TEXT_COLS + (w1 + x) / FONT_WIDTH) * sizeof(U16);
						win_z_buf_line_dec = whole_I64s;
						if (leading_pixels)
							win_z_buf_line_dec++;
						if (trailing_pixels)
							win_z_buf_line_dec++;
						win_z_buf_line_dec *= sizeof(U16);
						win_z_buf_line_inc = TEXT_COLS * sizeof(U16) - win_z_buf_line_dec;
					}
					kk = h1 * img->width_internal + w1;
					kk1 = (h1 + y) * dc->width_internal + x + w1;
					kk = (kk - bit_shift) & ~7 + bit_shift;
					bit_shift *= 8;
					if (win_z_buf_ptr)
						for (j = h1; j < h2; j++)
						{
							src = img->body + kk  & ~7;
							dst = dc->body  + kk1 & ~7;
							if (leading_pixels)
							{
								if (win_z_num >= *win_z_buf_ptr++)
								{
									if (bit_shift)
										*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask |
													   (*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift)) &
														~leading_pixel_mask;
									else
										*dst(I64 *)++ = *dst(I64 *)   &  leading_pixel_mask |
														*src(I64 *)++ & ~leading_pixel_mask;
								}
								else
								{
									src(I64 *)++;
									dst(I64 *)++;
								}
							}
							if (bit_shift)
								for (i = 0; i < whole_I64s; i++)
									if (win_z_num >= *win_z_buf_ptr++)
										*dst(I64 *)++ = *src(U64 *)++ >> bit_shift |
														*src(I64 *) << (64 - bit_shift);
									else
									{
										src(I64 *)++;
										dst(I64 *)++;
									}
							else
								for (i = 0; i < whole_I64s; i++)
									if (win_z_num >= *win_z_buf_ptr++)
										*dst(I64 *)++ = *src(I64 *)++;
									else
									{
										src(I64 *)++;
										dst(I64 *)++;
									}
							if (trailing_pixels && win_z_num >= *win_z_buf_ptr++)
							{
								if (bit_shift)
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
												 (*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift)) &
												  ~trailing_pixel_mask;
								else
									*dst(I64 *) = *dst(I64 *)   &  trailing_pixel_mask |
												  *src(I64 *)++ & ~trailing_pixel_mask;
							}
							kk  += img->width_internal;
							kk1 += dc->width_internal;
							if ((j + y) & 7 == 7)
								win_z_buf_ptr(U8 *) += win_z_buf_line_inc;
							else
								win_z_buf_ptr(U8 *) -= win_z_buf_line_dec;
						}
					else
						for (j = h2 - h1; j; j--)
						{
							src = img->body + kk  & ~7;
							dst = dc->body  + kk1 & ~7;
							if (leading_pixels)
							{
								if (bit_shift)
									*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask |
												   (*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift)) &
													~leading_pixel_mask;
								else
									*dst(I64 *)++ = *dst(I64 *)   &  leading_pixel_mask |
													*src(I64 *)++ & ~leading_pixel_mask;
							}
							if (bit_shift)
								for (i = 0; i < whole_I64s; i++)
									*dst(I64 *)++ = *src(U64 *)++ >> bit_shift |
													*src(I64 *) << (64 - bit_shift);
							else
								for (i = 0; i < whole_I64s; i++)
									*dst(I64 *)++ = *src(I64 *)++;

							if (trailing_pixels)
							{
								if (bit_shift)
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
												 (*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift)) &
												  ~trailing_pixel_mask;
								else
									*dst(I64 *) = *dst(I64 *)   &  trailing_pixel_mask |
												  *src(I64 *)++ & ~trailing_pixel_mask;
							}
							kk  += img->width_internal;
							kk1 += dc->width_internal;
						}
				}
				else
				{
here1a:
					k = h1 * img->width_internal;
					if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags & DCF_ON_TOP)
					{
						for (j = h1; j < h2; j++)
						{
							for (i = w1; i < w2; i++)
							{
								c = img->body[k + i];
								if (c != TRANSPARENT)
								{
									dc->color.c0.color = c;
									GrPlot0(dc, x + i, y + j);
								}
							}
							k += img->width_internal;
						}
					}
					else
					{
						win_z_num = win_task->win_z_num;
						win_z_buf_ptr = gr.win_z_buf(U8 *) +
										((h1 + y) / FONT_HEIGHT * TEXT_COLS + (w1 + x) / FONT_WIDTH) * sizeof(U16);
						win_z_buf_line_dec = whole_I64s;
						if (leading_pixels)
							win_z_buf_line_dec++;
						if (trailing_pixels)
							win_z_buf_line_dec++;
						win_z_buf_line_dec *= sizeof(U16);
						win_z_buf_line_inc = TEXT_COLS * sizeof(U16) - win_z_buf_line_dec;
						for (j = h1; j < h2; j++)
						{
							if (win_z_num >= *win_z_buf_ptr++)
								color_mask = TRUE;
							else
								color_mask = FALSE;
							for (i = w1; i < w2;)
							{
								if (color_mask)
								{
									c = img->body[k + i];
									if (c != TRANSPARENT)
									{
										dc->color.c0.color = c;
										GrPlot0(dc, x + i, y + j);
									}
								}
								if (!((++i + x) & 7) && i < w2)
								{
									if (win_z_num >= *win_z_buf_ptr++)
										color_mask = TRUE;
									else
										color_mask = FALSE;
								}
							}
							if ((j + y) & 7 == 7)
								win_z_buf_ptr(U8 *) += win_z_buf_line_inc;
							else
								win_z_buf_ptr(U8 *) -= win_z_buf_line_dec;
							k += img->width_internal;
						}
					}
					dc->color = color;
				}
				break;

			case ROPB_XOR:
				if (img->flags & DCF_NO_TRANSPARENTS)
				{
					if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags & DCF_ON_TOP)
						win_z_buf_ptr = NULL;
					else
					{
						win_z_num = win_task->win_z_num;
						win_z_buf_ptr = gr.win_z_buf(U8 *) +
										((h1 + y) / FONT_HEIGHT * TEXT_COLS + (w1 + x) / FONT_WIDTH) * sizeof(U16);
						win_z_buf_line_dec = whole_I64s;
						if (leading_pixels)
							win_z_buf_line_dec++;
						if (trailing_pixels)
							win_z_buf_line_dec++;
						win_z_buf_line_dec *= sizeof(U16);
						win_z_buf_line_inc = TEXT_COLS * sizeof(U16) - win_z_buf_line_dec;
					}
					kk = h1 * img->width_internal + w1;
					kk1 = (h1 + y) * dc->width_internal + x + w1;
					kk = (kk - bit_shift) & ~7 + bit_shift;
					bit_shift *= 8;
					if (win_z_buf_ptr)
						for (j = h1; j < h2; j++)
						{
							src = img->body + kk  & ~7;
							dst = dc->body  + kk1 & ~7;
							if (leading_pixels)
							{
								if (win_z_num >= *win_z_buf_ptr++)
								{
									if (bit_shift)
										*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask |
													   (*dst(I64 *) ^
															(*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift))) &
														~leading_pixel_mask;
									else
										*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask |
													   (*dst(I64 *) ^ *src(I64 *)++) & ~leading_pixel_mask;
								}
								else
								{
									src(I64 *)++;
									dst(I64 *)++;
								}
							}
							if (bit_shift)
								for (i = 0; i < whole_I64s; i++)
									if (win_z_num >= *win_z_buf_ptr++)
										*dst(I64 *)++ ^= *src(U64 *)++ >> bit_shift |
														 *src(I64 *) << (64 - bit_shift);
									else
									{
										src(I64 *)++;
										dst(I64 *)++;
									}
							else
								for (i = 0; i < whole_I64s; i++)
									if (win_z_num >= *win_z_buf_ptr++)
										*dst(I64 *)++ ^= *src(I64 *)++;
									else
									{
										src(I64 *)++;
										dst(I64 *)++;
									}
							if (trailing_pixels && win_z_num >= *win_z_buf_ptr++)
							{
								if (bit_shift)
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
												 (*dst(I64 *) ^
													(*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift))) &
												  ~trailing_pixel_mask;
								else
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
												 (*dst(I64 *) ^ *src(I64 *)++) & ~trailing_pixel_mask;
							}
							kk  += img->width_internal;
							kk1 += dc->width_internal;
							if ((j + y) & 7 == 7)
								win_z_buf_ptr(U8 *) += win_z_buf_line_inc;
							else
								win_z_buf_ptr(U8 *) -= win_z_buf_line_dec;
						}
					else
						for (j = h2 - h1; j; j--)
						{
							src = img->body + kk  & ~7;
							dst = dc->body  + kk1 & ~7;
							if (leading_pixels)
							{
								if (bit_shift)
									*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask |
												   (*dst(I64 *) ^
														(*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift))) &
													~leading_pixel_mask;
								else
									*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask |
												   (*dst(I64 *) ^ *src(I64 *)++) & ~leading_pixel_mask;
							}
							if (bit_shift)
								for (i = 0; i < whole_I64s; i++)
									*dst(I64 *)++ ^= *src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift);
							else
								for (i = 0; i < whole_I64s; i++)
									*dst(I64 *)++ ^= *src(I64 *)++;
							if (trailing_pixels)
							{
								if (bit_shift)
									*dst(I64 *) = *dst(I64 *)&trailing_pixel_mask |
												 (*dst(I64 *) ^
													(*src(U64 *)++ >> bit_shift | *src(I64 *) << (64 - bit_shift))) &
												  ~trailing_pixel_mask;
								else
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
												 (*dst(I64 *) ^ *src(I64 *)++) & ~trailing_pixel_mask;
							}
							kk  += img->width_internal;
							kk1 += dc->width_internal;
						}
				}
				else
					goto here1a;
				break;
		}
		dc->depth_buf	= db;
		dc->color		= old_color;
		return 1;
	}
	else
		return 0;
}

#help_index "Graphics/Device Contexts"

U8 *GrBitMap4ToBitMap8(U8 *dst, U8 *src, I64 src_size, I64 bkcolor)
{
	I64 c, k, i = src_size * 2, i1 = i >> 3;

	for (k = 0; k < i; k++)
	{
		c = 0;
		if (Bt(src,			 k)) c |= 1;
		if (Bt(src + i1,	 k)) c |= 2;
		if (Bt(src + i1 * 2, k)) c |= 4;
		if (Bt(src + i1 * 3, k)) c |= 8;
		if (c == bkcolor)		 c  = TRANSPARENT;
		*dst++ = c;
	}

	return dst;
}

U8 *GrBitMap1ToBitMap8(U8 *dst, U8 *src, I64 src_size, I64 bkcolor)
{
	I64 c, k, i = src_size * 8;

	for (k = 0; k < i; k++)
	{
		c = 0;
		if (Bt(src, k))	  c = COLOR_MONO;
		if (c == bkcolor) c = TRANSPARENT;
		*dst++ = c;
	}

	return dst;
}

public CDC *DCExt(CDC *dc=gr.dc, I64 x1, I64 y1, I64 x2, I64 y2, CTask *task=NULL)
{//Extract new device context rect from device context.
	CDC	  *res;
	CTask *win_task;

	if (x1 > x2)
		SwapI64(&x1, &x2);
	if (y1 > y2)
		SwapI64(&y1, &y2);
	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		win_task = dc->win_task;
		x1 += win_task->pix_left + win_task->scroll_x;
		y1 += win_task->pix_top  + win_task->scroll_y;
		x2 += win_task->pix_left + win_task->scroll_x;
		y2 += win_task->pix_top  + win_task->scroll_y;
	}
	res = DCNew(x2 - x1 + 1, y2 -y1 + 1, task);
	DCFill(res);
	GrBlot(res, -x1, -y1, dc);

	return res;
}

public CDC *DCDiff(CDC *base, CDC *update)
{//Trim to win of what has chged.
	I64  i, x1 = 0, y1 = 0, x2 = update->width - 1, y2 = update->height - 1; //inclusive
	U32 *ptr_base, *ptr_update;
	CDC *res;

	ptr_base   = base->body;
	ptr_update = update->body;
	while (y1 <= y2)
	{
		i = update->width >> 2;
		while (i--)
			if (*ptr_base++ != *ptr_update++)
				goto df_y2;
		i = update->width & 3;
		while (i--)
			if (*ptr_base(U8 *)++ != *ptr_update(U8 *)++)
				goto df_y2;
		y1++;
	}
	return NULL;
df_y2:
	ptr_base   = base->body	  + base->width_internal	* base->height;
	ptr_update = update->body + update->width_internal	* update->height;
	while (y1 < y2)
	{
		i = update->width >> 2;
		while (i--)
			if (*--ptr_base != *--ptr_update)
				goto df_x1;
		i = update->width & 3;
		while (i--)
			if (*--ptr_base(U8 *) != *--ptr_update(U8 *))
				goto df_x1;
		y2--;
	}
df_x1:
	while (x1 < x2)
	{
		for (i = y1; i <= y2; i++)
			if (GrPeek0(base, x1, i) != GrPeek0(update, x1, i))
				 goto df_x2;
		x1++;
	}
df_x2:
	while (x1 < x2)
	{
		for (i = y1; i <= y2; i++)
			if (GrPeek0(base, x2, i) != GrPeek0(update, x2, i))
				 goto df_done;
		x2--;
	}
df_done:
	res = DCExt(update, x1, y1, x2, y2);
	res->x0 = x1;
	res->y0 = y1;

	return res;
}

#help_index "Graphics/Char;Char/Graphics"

public I64 GrPutChar(CDC *dc=gr.dc, I64 x, I64 y, U8 ch)
{//2D. Clipping but not transformation.
	U8			reg *src, reg *dst, *font_ptr;
	I64				 i, m, leading_pixels, trailing_pixels, leading_pixel_mask, trailing_pixel_mask, 
					 j, k1, kk1, w1, h1, w2, h2, reg bit_shift, reg color_mask, dist;
	CColorROPU32	 color, c;
	CTask			*win_task;

	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		win_task = dc->win_task;
		x += win_task->scroll_x;
		y += win_task->scroll_y;
	}

	if (x < 0)
		w1 = -x;
	else
		w1 = 0;
	if (y < 0)
		h1 = -y;
	else
		h1 = 0;
	w2 = FONT_WIDTH;
	h2 = FONT_HEIGHT;

	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		x += win_task->pix_left;
		y += win_task->pix_top;
	}
	if (dc->flags & DCF_LOCATE_NEAREST)
	{
		dist = DistSqrI64(x + w2 >> 1, y + h2 >> 1, dc->cur_x, dc->cur_y);
		if (dist <= dc->nearest_dist)
			dc->nearest_dist = dist;
	}
	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		if (x + w1 < 0)
			w1 = -x;
		if (x + w2 > win_task->pix_right + 1)
			w2 = win_task->pix_right + 1 - x;

		if (y + h1 < 0)
			h1 = -y;
		if (y + h2 > win_task->pix_bottom + 1)
			h2 = win_task->pix_bottom + 1 - y;
	}
	if (x + w2 > dc->width)
		w2 = dc->width - x;
	if (y + h2>dc->height)
		h2 = dc->height - y;
	if (w1 < w2 <= FONT_WIDTH && h1 < h2 <= FONT_HEIGHT)
	{
		if (dc->flags & DCF_RECORD_EXTENTS)
		{
			if (x + w1 < dc->min_x)
				dc->min_x = x + w1;

			if (x + w2 - 1 > dc->max_x)
				dc->max_x = x + w2 - 1;

			if (y + h1	< dc->min_y)
				dc->min_y = y + h1;

			if (y + h2 - 1 > dc->max_y)
				dc->max_y = y + h2 - 1;
		}
		if (dc->flags & DCF_DONT_DRAW)
			return 1;
		color = dc->color;
		leading_pixels = -(w1 + x) & 7;
		if (!leading_pixels)
			leading_pixels = 8;
		leading_pixel_mask = gr.to_8_bits[0xFF >> leading_pixels];
		bit_shift = -x & 7;
		trailing_pixels = (x + w2) & 7;
		trailing_pixel_mask = gr.to_8_bits[0xFF << trailing_pixels & 0xFF];
		if (leading_pixels + trailing_pixels > w2 - w1)
		{
			leading_pixel_mask |= trailing_pixel_mask;
			trailing_pixels = 0;
		}
		font_ptr = &text.font(U8 *)[FONT_HEIGHT * ch + h1];
		if (color.c0.rop == ROPB_COLLISION)
		{
			m = w1 & (FONT_WIDTH - 1);
#assert FONT_WIDTH == 8
			color =dc->bkcolor.c0.color;
			for (i = w1; i < w2; i++, m++)
			{
				k1 = (h1 + y) * dc->width_internal + x;
				src = font_ptr;
				for (j = h2 - h1; j; j--)
				{
					c = dc->body[k1 + i];
					if (c != TRANSPARENT && c != color && Bt(src, m))
						dc->collision_count++;
					k1 += dc->width_internal;
					src++;
				}
			}
		}
		else
		{
			color_mask = gr.to_8_colors[color.c0.color];
			k1 = x + w1;
			kk1 = (h1 + y) * dc->width_internal + k1;
			if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags & DCF_ON_TOP)
			{
				if (leading_pixels)
				{
					dst = dc->body + kk1 & ~7;
					src = font_ptr;
					if (bit_shift)
						src--;
					switch [color.c0.rop]
					{
						case ROPB_EQU:
						case ROPB_MONO:
							for (j = h2 - h1; j; j--)
							{
								m = gr.to_8_bits[*src(U16 *) >> bit_shift & 0xFF];
								*dst(I64 *) = *dst(I64 *) & leading_pixel_mask |
											 (color_mask & m | *dst(I64 *) & ~m) & ~leading_pixel_mask;
								src++;
								dst += dc->width_internal;
							}
							break;

						case ROPB_XOR:
							if (color_mask)
							{
								for (j = h2 - h1; j; j--)
								{
									*dst(I64 *) = *dst(I64 *) & leading_pixel_mask |
												 (*dst(I64 *) ^ gr.to_8_bits[*src(U16 *) >> bit_shift & 0xFF]) &
												  ~leading_pixel_mask;
									src++;
									dst += dc->width_internal;
								}
							}
							break;
					}
					kk1 += 8;
				}
				if (trailing_pixels)
				{
					dst = dc->body + kk1 & ~7;
					src = font_ptr + 1;
					if (bit_shift)
						src--;
					switch [color.c0.rop]
					{
						case ROPB_EQU:
						case ROPB_MONO:
							for (j = h2 - h1; j; j--)
							{
								m = gr.to_8_bits[*src(U16 *) >> bit_shift & 0xFF];
								*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
											 (color_mask & m | *dst(I64 *) & ~m) & ~trailing_pixel_mask;
								src++;
								dst += dc->width_internal;
							}
							break;

						case ROPB_XOR:
							if (color_mask)
								for (j = h2 - h1; j; j--)
								{
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
												 (*dst(I64 *) ^ gr.to_8_bits[*src(U16 *) >> bit_shift & 0xFF]) &
												  ~trailing_pixel_mask;
									src++;
									dst += dc->width_internal;
								}
							break;
					}
				}
			}
			else
			{
				if (leading_pixels)
				{
					dst = dc->body + kk1 & ~7;
					src = font_ptr;
					if (bit_shift)
						src--;
					switch [color.c0.rop]
					{
						case ROPB_EQU:
						case ROPB_MONO:
							for (j = h1; j < h2; j++)
							{
								if (!IsPixCovered0(win_task, k1, y + j))
								{
									m = gr.to_8_bits[*src(U16 *) >> bit_shift & 0xFF];
									*dst(I64 *) = *dst(I64 *) & leading_pixel_mask |
												 (color_mask & m | *dst(I64 *) & ~m) & ~leading_pixel_mask;
								}
								src++;
								dst += dc->width_internal;
							}
							break;

						case ROPB_XOR:
							if (color_mask)
								for (j = h1; j < h2; j++)
								{
									if (!IsPixCovered0(win_task, k1, y + j))
										*dst(I64 *) = *dst(I64 *) & leading_pixel_mask |
													 (*dst(I64 *) ^ gr.to_8_bits[*src(U16 *) >> bit_shift & 0xFF]) &
													  ~leading_pixel_mask;
									src++;
									dst += dc->width_internal;
								}
							break;
					}
					k1 += 8;
					kk1 += 8;
				}
				if (trailing_pixels)
				{
					dst = dc->body + kk1 & ~7;
					src = font_ptr + 1;
					if (bit_shift)
						src--;
					switch [color.c0.rop]
					{
						case ROPB_EQU:
						case ROPB_MONO:
							for (j = h1; j < h2; j++)
							{
								if (!IsPixCovered0(win_task, k1, y + j))
								{
									m = gr.to_8_bits[*src(U16 *) >> bit_shift & 0xFF];
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
												 (color_mask & m | *dst(I64 *) & ~m) & ~trailing_pixel_mask;
								}
								src++;
								dst += dc->width_internal;
							}
							break;

						case ROPB_XOR:
							if (color_mask)
								for (j  =h1; j < h2; j++)
								{
									if (!IsPixCovered0(win_task, k1, y + j))
										*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
													 (*dst(I64 *) ^ gr.to_8_bits[*src(U16 *) >> bit_shift & 0xFF]) &
													  ~trailing_pixel_mask;
									src++;
									dst += dc->width_internal;
								}
							break;
					}
				}
			}
		}
		return 1;
	}
	else
		return 0;
}

I64 GrPutS(CDC *dc=gr.dc, I64 x, I64 y, U8 *_s)
{//Use $LK,"GrPrint",A="MN:GrPrint"$()
	I64 x0, sx = 0, sy = 0, res;

	if (!_s)
		return 0;
	x0 = x;
	res = 0;
	while (*_s)
	{
		if (*_s == '\n')
		{
			x = x0;
			y += FONT_HEIGHT;
			_s++;
		}
		else if (*_s == '\t')
		{
			x = x0 + CeilU64(x - x0 + FONT_WIDTH, 8 * FONT_WIDTH);
			_s++;
		}
		else if (*_s(U32 *) == '$$SY,')
		{
			if (_s[4] == '-')
			{
				_s++;
				sy = '0' - _s[4];
			}
			else
				sy = _s[4] - '0';
			_s += 6;
		}
		else if (*_s(U32 *) == '$$SX,')
		{
			if (_s[4] == '-')
			{
				_s++;
				sx = '0' - _s[4];
			}
			else
				sx = _s[4] - '0';
			_s += 6;
		}
		else
		{
			res += GrPutChar(dc, x + sx, y + sy, *_s);
			x += FONT_WIDTH;
			_s++;
		}
	}

	return res;
}

I64 GrVPutS(CDC *dc=gr.dc, I64 x, I64 y, U8 *_s)
{//Vertical Text.  Use $LK,"GrVPrint",A="MN:GrVPrint"$()
	I64 y0, sx = 0, sy = 0, res;
	U8  buf[2];

	if (!_s)
		return 0;
	y0 = y;
	res = 0;
	buf[1] = 0;
	while (*_s)
	{
		if (*_s == '\n')
		{
			y = y0;
			x += FONT_WIDTH;
			_s++;
		}
		else if (*_s == '\t')
		{
			y = y0 + CeilU64(y - y0 + FONT_HEIGHT, 8 * FONT_HEIGHT);
			_s++;
		}
		else if (*_s(U32 *) == '$$SY,')
		{
			if (_s[4] == '-')
			{
				_s++;
				sx = '0' - _s[4];
			}
			else
				sx = _s[4] - '0';
			_s += 6;
		}
		else if (*_s(U32 *) == '$$SX,')
		{
			if (_s[4] == '-')
			{
				_s++;
				sy = '0' - _s[4];
			}
			else
				sy = _s[4] - '0';
			_s += 6;
		}
		else
		{
			*buf = *_s++;
			res += GrPutS(dc, x, y, buf);
			y += FONT_HEIGHT;
		}
	}

	return res;
}

public I64 GrPrint(CDC *dc=gr.dc, I64 x, I64 y, U8 *format, ...)
{//2D. Clipping but not transformation.
	I64 res;
	U8 *buf = StrPrintJoin(NULL, format, argc, argv);

	res = GrPutS(dc, x, y, buf);
	Free(buf);

	return res;
}

public I64 GrVPrint(CDC *dc=gr.dc, I64 x, I64 y, U8 *format, ...)
{//2D. Vertical text. Clipping but not transformation.
	I64 res;
	U8 *buf = StrPrintJoin(NULL, format, argc, argv);

	res = GrVPutS(dc, x, y, buf);
	Free(buf);

	return res;
}

#help_index "Graphics"
public I64 GrRect(CDC *dc=gr.dc, I64 x, I64 y, I64 w, I64 h)
{//2D. Width Height. Clipping but not transformation.
//Returns count of pixs changed.
	I64				 i, res = 0, j, k1, kk1, w1, h1, w2, h2, dist, 
					 leading_pixels, original_leading_pixels, whole_I64s, 
					 trailing_pixels, leading_pixel_mask, trailing_pixel_mask, 
					 win_z_buf_line_inc, win_z_buf_line_dec, win_z_num, color_mask;
	U8			reg *dst;
	U16			reg *win_z_buf_ptr;
	CColorROPU32	 color, c, dither_colors;
	Bool			 dither, probability_dither, was_timer_rand = Bts(&Fs->task_flags, TASKf_NONTIMER_RAND);
	CTask			*win_task;

	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		win_task = dc->win_task;
		x += win_task->scroll_x;
		y += win_task->scroll_y;
	}

	if (x < 0)
		w1 = -x;
	else
		w1 = 0;
	if (y < 0)
		h1 = -y;
	else
		h1 = 0;
	w2 = w;
	h2 = h;

	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		x += win_task->pix_left;
		y += win_task->pix_top;
	}
	if (dc->flags & DCF_LOCATE_NEAREST)
	{//TODO:Untested
		if (x <= dc->cur_x <= x + w && y <= dc->cur_y <= y + h)
			dist = 0;
		else
			dist = DistSqrI64(x + w >> 1, y + h >> 1, dc->cur_x, dc->cur_y);
		if (dist <= dc->nearest_dist)
			dc->nearest_dist = dist;
	}
	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		if (x + w1 < 0)
			w1 = -x;
		if (x + w2 > win_task->pix_right + 1)
			w2 = win_task->pix_right + 1 - x;

		if (y + h1 < 0)
			h1 = -y;
		if (y + h2 > win_task->pix_bottom + 1)
			h2 = win_task->pix_bottom + 1 - y;
	}
	if (x + w2 > dc->width)
		w2 = dc->width - x;

	if (y + h2 > dc->height)
		h2 = dc->height - y;

	if (w1 < w2 <= w && h1 < h2 <= h)
	{
		if (dc->flags & DCF_RECORD_EXTENTS)
		{
			if (x + w1	< dc->min_x)
				dc->min_x = x + w1;

			if (x + w2 - 1 > dc->max_x)
				dc->max_x = x + w2 - 1;

			if (y + h1	< dc->min_y)
				dc->min_y = y + h1;

			if (y + h2 - 1 > dc->max_y)
				dc->max_y = y + h2 - 1;
		}
		if (dc->flags & DCF_DONT_DRAW)
			return TRUE;
		color = dc->color;
		if (color.c1.rop & (ROPBF_DITHER | ROPBF_PROBABILITY_DITHER))
		{
			dither = TRUE;
			if (color.c1.rop & ROPBF_PROBABILITY_DITHER)
			{
				probability_dither = TRUE;
				color.c1.rop = color.c0.rop;
				dither_colors = color;
			}
			else
			{
				probability_dither = FALSE;
				color.c1.rop = color.c0.rop;
			}
		}
		else
			dither = FALSE;
		original_leading_pixels = leading_pixels = -(w1 + x) & 7;
		leading_pixel_mask = gr.to_8_bits[0xFF >> leading_pixels];
		whole_I64s = (w2 - w1 - leading_pixels) >> 3;
		if (whole_I64s < 0)
			whole_I64s = 0;
		trailing_pixels = (x + w2) & 7;
		trailing_pixel_mask = gr.to_8_bits[0xFF << trailing_pixels & 0xFF];
		if (leading_pixels + trailing_pixels > w2 - w1)
		{
			leading_pixel_mask |= trailing_pixel_mask;
			leading_pixels = w2 - w1; //Correct so it's right for res.
			trailing_pixels = 0;
		}
		if (color.c0.rop == ROPB_COLLISION)
		{//TODO: Might want to check win_z_buf
			color = dc->bkcolor.c0.color;
			k1 = (h1 + y) * dc->width_internal + x;
			res = -dc->collision_count;
			for (j = h2 - h1; j; j--)
			{
				for (i = w1; i < w2; i++)
				{
					c = dc->body[k1 + i];
					if (c != TRANSPARENT && c != color)
						dc->collision_count++;
				}
				k1 += dc->width_internal;
			}
			res += dc->collision_count;
		}
		else
		{
			if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags & DCF_ON_TOP)
				win_z_buf_ptr = NULL;
			else
			{
				win_z_num = win_task->win_z_num;
				win_z_buf_ptr = gr.win_z_buf(U8 *) +
								((h1 + y) / FONT_HEIGHT * TEXT_COLS + (w1 + x) / FONT_WIDTH) * sizeof(U16);
				win_z_buf_line_dec = whole_I64s;
				if (leading_pixels)
					win_z_buf_line_dec++;
				if (trailing_pixels)
					win_z_buf_line_dec++;
				win_z_buf_line_dec *= sizeof(U16);
				win_z_buf_line_inc = TEXT_COLS * sizeof(U16) - win_z_buf_line_dec;
			}
			kk1 = (h1 + y) * dc->width_internal + x + w1;
			if (dither)
			{
				if (probability_dither)
				{
					if (RandU16 < dc->dither_probability_u16)
						color.c0 = dither_colors.c1;
					else
						color.c0 = dither_colors.c0;
					switch [color.c0.rop]
					{
						case ROPB_EQU:
						case ROPB_MONO:
							if (win_z_buf_ptr)
							{
								res = 0;
								for (j = h1; j < h2; j++)
								{
									color_mask = gr.to_8_colors[color.c0.color];
									dst = dc->body + kk1 & ~7;
									if (leading_pixels)
									{
										if (win_z_num >= *win_z_buf_ptr++)
										{
											*dst(I64 *) = *dst(I64 *) & leading_pixel_mask | color_mask & ~leading_pixel_mask;
											res += leading_pixels;
										}
										dst(I64 *)++;
									}
									for (i = 0; i < whole_I64s; i++, dst(I64 *)++)
										if (win_z_num >= *win_z_buf_ptr++)
										{
											*dst(I64 *) = color_mask;
											res += 8;
										}
									if (trailing_pixels && win_z_num >= *win_z_buf_ptr++)
									{
										*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask | color_mask & ~trailing_pixel_mask;
										res += trailing_pixels;
									}
									if ((j + y) & 7 == 7)
										win_z_buf_ptr(U8 *) += win_z_buf_line_inc;
									else
										win_z_buf_ptr(U8 *) -= win_z_buf_line_dec;
									kk1 += dc->width_internal;
									if (RandU16 < dc->dither_probability_u16)
										color.c0 = dither_colors.c1;
									else
										color.c0 = dither_colors.c0;
								}
							}
							else
							{
								for (j = h2 - h1; j; j--)
								{
									color_mask = gr.to_8_colors[color.c0.color];
									dst = dc->body + kk1 & ~7;
									if (leading_pixels)
										*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask | color_mask & ~leading_pixel_mask;
									for (i = 0; i < whole_I64s; i++, dst(I64 *)++)
										*dst(I64 *) = color_mask;
									if (trailing_pixels)
										*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask | color_mask & ~trailing_pixel_mask;
									kk1 += dc->width_internal;
									if (RandU16 < dc->dither_probability_u16)
										color.c0 = dither_colors.c1;
									else
										color.c0 = dither_colors.c0;
								}
								res = (h2 - h1) * (w2 - w1);
							}
							BEqual(&Fs->task_flags, TASKf_NONTIMER_RAND, was_timer_rand);

							break;

						case ROPB_XOR:
							if (win_z_buf_ptr)
							{
								res = 0;
								for (j = h1; j < h2; j++)
								{
									color_mask = gr.to_8_colors[color.c0.color];
									dst = dc->body + kk1 & ~7;
									if (leading_pixels)
									{
										if (win_z_num >= *win_z_buf_ptr++)
										{
											*dst(I64 *) = *dst(I64 *) & leading_pixel_mask |
														  *dst(I64 *) ^ color_mask & ~leading_pixel_mask;
											res += leading_pixels;
										}
										dst(I64 *)++;
									}
									for (i = 0; i < whole_I64s; i++, dst(I64 *)++)
										if (win_z_num >= *win_z_buf_ptr++)
										{
											*dst(I64 *) ^= color_mask;
											res += 8;
										}
									if (trailing_pixels && win_z_num >= *win_z_buf_ptr++)
									{
										*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
													  *dst(I64 *) ^ color_mask & ~trailing_pixel_mask;
										res += trailing_pixels;
									}
									if ((j + y) & 7 == 7)
										win_z_buf_ptr(U8 *) += win_z_buf_line_inc;
									else
										win_z_buf_ptr(U8 *) -= win_z_buf_line_dec;
									kk1 += dc->width_internal;
									if (RandU16 < dc->dither_probability_u16)
										color.c0 = dither_colors.c1;
									else
										color.c0 = dither_colors.c0;
								}
							}
							else
							{
								for (j = h2 - h1; j; j--)
								{
									color_mask = gr.to_8_colors[color.c0.color];
									dst = dc->body + kk1 & ~7;
									if (leading_pixels)
										*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask |
														*dst(I64 *) ^ color_mask & ~leading_pixel_mask;
									for (i = 0; i < whole_I64s; i++, dst(I64 *)++)
										*dst(I64 *) ^= color_mask;
									if (trailing_pixels)
										*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
													  *dst(I64 *) ^ color_mask & ~trailing_pixel_mask;
									kk1 += dc->width_internal;
									if (RandU16 < dc->dither_probability_u16)
										color.c0 = dither_colors.c1;
									else
										color.c0 = dither_colors.c0;
								}
								res = (h2 - h1) * (w2 - w1);
							}
							BEqual(&Fs->task_flags, TASKf_NONTIMER_RAND, was_timer_rand);

							break;
					}
				}
				else
				{
					BEqual(&Fs->task_flags, TASKf_NONTIMER_RAND, was_timer_rand);

					if (((x + w1 - original_leading_pixels) ^ (y + h1)) & 1)
						SwapU16(&color.c0, &color.c1);
					switch [color.c0.rop]
					{
						case ROPB_EQU:
						case ROPB_MONO:
							if (win_z_buf_ptr)
							{
								res = 0;
								for (j = h1; j < h2; j++)
								{
									color_mask = gr.to_8_bits[0x55] & gr.to_8_colors[color.c0.color] |
												 gr.to_8_bits[0xAA] & gr.to_8_colors[color.c1.color];
									dst = dc->body + kk1 & ~7;
									if (leading_pixels)
									{
										if (win_z_num >= *win_z_buf_ptr++)
										{
											*dst(I64 *) = *dst(I64 *) & leading_pixel_mask | color_mask & ~leading_pixel_mask;
											res += leading_pixels;
										}
										dst(I64 *)++;
									}
									for (i = 0; i < whole_I64s; i++, dst(I64 *)++)
										if (win_z_num >= *win_z_buf_ptr++)
										{
											*dst(I64 *) = color_mask;
											res += 8;
										}
									if (trailing_pixels && win_z_num >= *win_z_buf_ptr++)
									{
										*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask | color_mask & ~trailing_pixel_mask;
										res += trailing_pixels;
									}
									if ((j + y) & 7 == 7)
										win_z_buf_ptr(U8 *) += win_z_buf_line_inc;
									else
										win_z_buf_ptr(U8 *) -= win_z_buf_line_dec;
									kk1 += dc->width_internal;
									SwapU16(&color.c0, &color.c1);
								}
							}
							else
							{
								for (j = h2 - h1; j; j--)
								{
									color_mask = gr.to_8_bits[0x55] & gr.to_8_colors[color.c0.color] |
												 gr.to_8_bits[0xAA] & gr.to_8_colors[color.c1.color];
									dst = dc->body + kk1 & ~7;
									if (leading_pixels)
										*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask | color_mask & ~leading_pixel_mask;
									for (i = 0; i < whole_I64s; i++, dst(I64 *)++)
										*dst(I64 *) = color_mask;
									if (trailing_pixels)
										*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask | color_mask & ~trailing_pixel_mask;
									kk1 += dc->width_internal;
									SwapU16(&color.c0, &color.c1);
								}
								res = (h2 - h1) * (w2 - w1);
							}
							break;

						case ROPB_XOR:
							if (win_z_buf_ptr)
							{
								res = 0;
								for (j = h1; j < h2; j++)
								{
									color_mask = gr.to_8_bits[0x55] & gr.to_8_colors[color.c0.color] |
												 gr.to_8_bits[0xAA] & gr.to_8_colors[color.c1.color];
									dst = dc->body + kk1 & ~7;
									if (leading_pixels)
									{
										if (win_z_num >= *win_z_buf_ptr++)
										{
											*dst(I64 *) = *dst(I64 *) & leading_pixel_mask |
														  *dst(I64 *) ^ color_mask & ~leading_pixel_mask;
											res += leading_pixels;
										}
										dst(I64 *)++;
									}
									for (i = 0; i < whole_I64s; i++, dst(I64 *)++)
										if (win_z_num >= *win_z_buf_ptr++)
										{
											*dst(I64 *) ^= color_mask;
											res += 8;
										}
									if (trailing_pixels && win_z_num >= *win_z_buf_ptr++)
									{
										*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
													  *dst(I64 *) ^ color_mask & ~trailing_pixel_mask;
										res += trailing_pixels;
									}
									if ((j + y) & 7 == 7)
										win_z_buf_ptr(U8 *) += win_z_buf_line_inc;
									else
										win_z_buf_ptr(U8 *) -= win_z_buf_line_dec;
									kk1 += dc->width_internal;
									SwapU16(&color.c0, &color.c1);
								}
							}
							else
							{
								for (j = h2 - h1; j; j--)
								{
									color_mask = gr.to_8_bits[0x55] & gr.to_8_colors[color.c0.color] |
												 gr.to_8_bits[0xAA] & gr.to_8_colors[color.c1.color];
									dst = dc->body + kk1 & ~7;
									if (leading_pixels)
										*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask |
														*dst(I64 *) ^ color_mask & ~leading_pixel_mask;
									for (i = 0; i < whole_I64s; i++, dst(I64 *)++)
										*dst(I64 *) ^= color_mask;
									if (trailing_pixels)
										*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
													  *dst(I64 *) ^ color_mask & ~trailing_pixel_mask;
									kk1 += dc->width_internal;
									SwapU16(&color.c0, &color.c1);
								}
								res = (h2 - h1) * (w2 - w1);
							}
							break;
					}
				}
			}
			else
			{
				BEqual(&Fs->task_flags, TASKf_NONTIMER_RAND, was_timer_rand);

				color_mask = gr.to_8_colors[color.c0.color];
				switch [color.c0.rop]
				{
					case ROPB_EQU:
					case ROPB_MONO:
						if (win_z_buf_ptr)
						{
							res = 0;
							for (j = h1; j < h2; j++)
							{
								dst = dc->body + kk1 & ~7;
								if (leading_pixels)
								{
									if (win_z_num >= *win_z_buf_ptr++)
									{
										*dst(I64 *) = *dst(I64 *) & leading_pixel_mask | color_mask & ~leading_pixel_mask;
										res += leading_pixels;
									}
									dst(I64 *)++;
								}
								for (i = 0; i < whole_I64s; i++, dst(I64 *)++)
									if (win_z_num >= *win_z_buf_ptr++)
									{
										*dst(I64 *) = color_mask;
										res += 8;
									}
								if (trailing_pixels && win_z_num >= *win_z_buf_ptr++)
								{
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask | color_mask & ~trailing_pixel_mask;
									res += trailing_pixels;
								}
								if ((j + y) & 7 == 7)
									win_z_buf_ptr(U8 *) += win_z_buf_line_inc;
								else
									win_z_buf_ptr(U8 *) -= win_z_buf_line_dec;
								kk1 += dc->width_internal;
							}
						}
						else
						{
							for (j = h2 - h1; j; j--)
							{
								dst(I64 *) = dc->body + kk1 & ~7;
								if (leading_pixels)
									*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask | color_mask & ~leading_pixel_mask;
								for (i = 0; i < whole_I64s; i++, dst(I64 *)++)
									*dst(I64 *) = color_mask;
								if (trailing_pixels)
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask | color_mask & ~trailing_pixel_mask;
								kk1 += dc->width_internal;
							}
							res = (h2 - h1) * (w2 - w1);
						}
						break;

					case ROPB_XOR:
						if (win_z_buf_ptr)
						{
							res = 0;
							for (j = h1; j < h2; j++)
							{
								dst = dc->body + kk1 & ~7;
								if (leading_pixels)
								{
									if (win_z_num >= *win_z_buf_ptr++)
									{
										*dst(I64 *) = *dst(I64 *) & leading_pixel_mask |
													  *dst(I64 *) ^ color_mask & ~leading_pixel_mask;
										res += leading_pixels;
									}
									dst(I64 *)++;
								}
								for (i = 0; i < whole_I64s; i++, dst(I64 *)++)
									if (win_z_num >= *win_z_buf_ptr++)
									{
										*dst(I64 *) ^= color_mask;
										res += 8;
									}
								if (trailing_pixels && win_z_num >= *win_z_buf_ptr++)
								{
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
												  *dst(I64 *) ^ color_mask & ~trailing_pixel_mask;
									res += trailing_pixels;
								}
								if ((j + y) & 7 == 7)
									win_z_buf_ptr(U8 *) += win_z_buf_line_inc;
								else
									win_z_buf_ptr(U8 *) -= win_z_buf_line_dec;
								kk1 += dc->width_internal;
							}
						}
						else
						{
							for (j = h2 - h1; j; j--)
							{
								dst = dc->body + kk1 & ~7;
								if (leading_pixels)
									*dst(I64 *)++ = *dst(I64 *) & leading_pixel_mask |
													*dst(I64 *) ^ color_mask & ~leading_pixel_mask;
								for (i = 0; i < whole_I64s; i++, dst(I64 *)++)
									*dst(I64 *) ^= color_mask;
								if (trailing_pixels)
									*dst(I64 *) = *dst(I64 *) & trailing_pixel_mask |
												  *dst(I64 *) ^ color_mask & ~trailing_pixel_mask;
								kk1 += dc->width_internal;
							}
							res = (h2 - h1) * (w2 - w1);
						}
						break;
				}
			}
		}
	}

	return res;
}

I64 GrRayLenMinus(CDC *dc, I64 x, I64 y)
{//Returns count of pixs changed
	I64		 res = 0, c, x3, y3, d;
	U8		*dst, *dst2;
	Bool	 not_color = ToBool(dc->flags & DCF_FILL_NOT_COLOR);
	CTask	*win_task;

	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		win_task = dc->win_task;
		x += win_task->scroll_x;
		y += win_task->scroll_y;
	}
	x3 = x;
	y3 = y;
	if (x3 < 0 || y3 < 0)
		goto gr_done;
	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		x3 += win_task->pix_left;
		y3 += win_task->pix_top;
		if (	!(0 <= x3 <= win_task->pix_right)  ||
				!(0 <= y3 <= win_task->pix_bottom) ||
				!(dc->flags & DCF_ON_TOP) && IsPixCovered0(win_task, x3, y3))
			goto gr_done;
	}
	if (x3 >= dc->width || y3 >= dc->height)
		goto gr_done;

	d = y3 * dc->width_internal;
	dst2 = dc->body + d;
	while (TRUE)
	{
		x3 = x;
		if (x3 & (FONT_WIDTH - 1) == FONT_WIDTH - 1)
		{
			if (dc->flags & DCF_SCREEN_BITMAP)
			{
				if (x3 < 0)
					break;
				x3 += win_task->pix_left;
				if (	!(0 <= x3 <= win_task->pix_right) || x3 >= dc->width ||
						!(dc->flags & DCF_ON_TOP) && IsPixCovered0(win_task, x3, y3))
					break;
			}
			else
				if (!(0 <= x3 < dc->width))
					break;
		}
		else if (dc->flags & DCF_SCREEN_BITMAP)
			x3 += win_task->pix_left;
		dst = dst2 + x3;
		c = *dst;
		if (not_color)
		{
			if (c != dc->color2)
			{
				res++;
				x--;
			}
			else
				break;
		}
		else
		{
			if (c == dc->color2)
			{
				res++;
				x--;
			}
			else
				break;
		}
	}

	return res;
gr_done:
	return 0;
}

I64 GrRayLen(CDC *dc, I64 *x1, I64 y, I64 z=0, I32 *db=NULL)
{
//Returns count of pixs changed
	I64				 res = 0, d, x = *x1, x2, x3, y3, dist;
	Bool			 plot, dither, probability_dither, 
					 not_color = ToBool(dc->flags & DCF_FILL_NOT_COLOR), 
					 was_timer_rand = Bts(&Fs->task_flags, TASKf_NONTIMER_RAND);
	U8				*dst, *dst2;
	CColorROPU32	 c, c2, color = dc->color, bkcolor = dc->bkcolor;
	I32				*db2;
	CTask			*win_task;


	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		win_task = dc->win_task;
		x += win_task->scroll_x;
		y += win_task->scroll_y;
		z += win_task->scroll_z;
	}
	x2 = x;
	x3 = x;
	y3 = y;
	if (x3 < 0 || y3 < 0)
		goto gr_done;
	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		x3 += win_task->pix_left;
		y3 += win_task->pix_top;
		if (	!(0 <= x3 <= win_task->pix_right)  ||
				!(0 <= y3 <= win_task->pix_bottom) ||
				!(dc->flags & DCF_ON_TOP) && IsPixCovered0(win_task, x3, y3))
			goto gr_done;
	}
	if (x3 >= dc->width || y3 >= dc->height)
		goto gr_done;

	d = dc->width_internal * y3;
	if (db)
		db += d;

	color = dc->color;
	if (color.c1.rop & (ROPBF_DITHER | ROPBF_PROBABILITY_DITHER))
	{
		dither = TRUE;
		if (color.c1.rop & ROPBF_PROBABILITY_DITHER)
		{
			probability_dither = TRUE;
			color.c1.rop = color.c0.rop;
		}
		else
		{
			probability_dither = FALSE;
			color.c1.rop = color.c0.rop;
		}
	}
	else
		dither = FALSE;
	dst2 = dc->body + d;
	while (TRUE)
	{
		x3 = x;
		if (!(x3 & (FONT_WIDTH - 1)))
		{
			if (dc->flags & DCF_SCREEN_BITMAP)
			{
				if (x3 < 0)
					break;
				x3 += win_task->pix_left;
				if (	!(0 <= x3 <= win_task->pix_right) || x3 >= dc->width ||
						!(dc->flags & DCF_ON_TOP) && IsPixCovered0(win_task, x3, y3))
					break;
			}
			else
			{
				if (!(0 <= x3 < dc->width))
					break;
			}
		}
		else if (dc->flags & DCF_SCREEN_BITMAP)
			x3 += win_task->pix_left;

		dst = dst2 + x3;

		c = *dst;
		if (db)
		{
			db2 = db + x3;
			if (0 <= z <= *db2)
			{
				*db2 = z;
				plot = TRUE;
			}
			else
				plot = FALSE;
		}
		else
			plot = TRUE;

		if ((not_color && c != dc->color2 || !not_color && c == dc->color2) && plot)
		{
			if (dc->flags & DCF_LOCATE_NEAREST)
			{
				dist = DistSqrI64(x3, y3, dc->cur_x, dc->cur_y);
				if (dist <= dc->nearest_dist)
					dc->nearest_dist = dist;
			}
			if (dc->flags & DCF_RECORD_EXTENTS)
			{
				if (x3 < dc->min_x)
					dc->min_x = x3;

				if (x3 > dc->max_x)
					dc->max_x = x3;

				if (y3 < dc->min_y)
					dc->min_y = y3;

				if (y3 > dc->max_y)
					dc->max_y = y3;
			}
			dst = dst2 + x3;

			c = color.c0.color;
			if (dither)
			{
				if (probability_dither)
				{
					if (RandU16 < dc->dither_probability_u16)
						c = color.c1.color;
				}
				else
					if ((x3 ^ y3) & 1)
						c = color.c1.color;
			}
			switch [color.c0.rop]
			{
				case ROPB_EQU:
				case ROPB_MONO:
					*dst = c;
					break;

				case ROPB_COLLISION:
					c2 = *dst;
					if (c2 != TRANSPARENT && c2 != bkcolor.c0.color)
						dc->collision_count++;
					break;

				case ROPB_XOR:
					*dst ^= c;
					break;
			}
			res++;
			x++;
		}
		else
			break;
	}
	if (dc->flags & DCF_SCREEN_BITMAP)
		*x1 = x - 1 - win_task->scroll_x;
	else
		*x1 = x - 1;
	x = x2 - 1;
	while (TRUE)
	{
		x3 = x;
		if (x3 & (FONT_WIDTH - 1) == FONT_WIDTH - 1)
		{
			if (dc->flags & DCF_SCREEN_BITMAP)
			{
				if (x3 < 0)
					break;
				x3 += win_task->pix_left;
				if (	!(0 <= x3 <= win_task->pix_right) || x3 >= dc->width ||
						!(dc->flags & DCF_ON_TOP) && IsPixCovered0(win_task, x3, y3))
					break;
			}
			else
				if (!(0 <= x3 < dc->width))
					break;
		}
		else if (dc->flags & DCF_SCREEN_BITMAP)
			x3 += win_task->pix_left;

		dst = dst2 + x3;
		c = *dst;
		if (db)
		{
			db2 = db + x3;
			if (0 <= z <= *db2)
			{
				*db2 = z;
				plot = TRUE;
			}
			else
				plot = FALSE;
		}
		else
			plot = TRUE;

		if ((not_color && c != dc->color2 || !not_color && c == dc->color2) && plot)
		{
			if (dc->flags & DCF_LOCATE_NEAREST)
			{
				dist = DistSqrI64(x3, y3, dc->cur_x, dc->cur_y);
				if (dist <= dc->nearest_dist)
					dc->nearest_dist = dist;
			}
			if (dc->flags & DCF_RECORD_EXTENTS)
			{
				if (x3 < dc->min_x)
					dc->min_x = x3;
				if (x3 > dc->max_x)
					dc->max_x = x3;
				if (y3 < dc->min_y)
					dc->min_y = y3;
				if (y3 > dc->max_y)
					dc->max_y = y3;
			}
			dst = dst2 + x3;

			c = color.c0.color;
			if (dither)
			{
				if (probability_dither)
				{
					if (RandU16 < dc->dither_probability_u16)
						c = color.c1.color;
				}
				else
					if ((x3 ^ y3) & 1)
						c = color.c1.color;
			}
			switch [color.c0.rop]
			{
				case ROPB_EQU:
				case ROPB_MONO:
					*dst = c;
					break;

				case ROPB_COLLISION:
					c2 = *dst;
					if (c2 != TRANSPARENT && c2 != bkcolor.c0.color)
						dc->collision_count++;
					break;

				case ROPB_XOR:
					*dst ^= c;
					break;
			}
			res++;
			x--;
		}
		else
			break;
	}
	BEqual(&Fs->task_flags, TASKf_NONTIMER_RAND, was_timer_rand);

	return res;
gr_done:
	BEqual(&Fs->task_flags, TASKf_NONTIMER_RAND, was_timer_rand);

	return 0;
}

public I64 GrHLine(CDC *dc=gr.dc, I64 x1, I64 x2, I64 y, I64 z1=0, I64 z2=0)
{//3D. No transformation or thick.
//Returns count of pixs changed
	//Uses $LK,"fixed-point",A="FI:::/Demo/Lectures/FixedPoint.CC"$.
	I64				 dist, dx, dz, z, res = 0, i, j, d;
	U8				*dst;
	CColorROPU32	 c, c2, color = dc->color, bkcolor = dc->bkcolor, dither_colors;
	I32				*db;
	Bool			 plot = TRUE, char_clear, dither, probability_dither,
					 was_timer_rand = Bts(&Fs->task_flags, TASKf_NONTIMER_RAND);
	CTask			*win_task;


	if (!dc->depth_buf)
	{
		if (x2 < x1)
			SwapI64(&x1, &x2);
		return GrRect(dc, x1, y, x2 - x1 + 1, 1);
	}

	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		win_task = dc->win_task;
		x1 += win_task->scroll_x;
		x2 += win_task->scroll_x;
		y  += win_task->scroll_y;
		z1 += win_task->scroll_z;
		z2 += win_task->scroll_z;
	}
	if (dc->flags & DCF_RECORD_EXTENTS)
	{
		if (x1 < dc->min_x)
			dc->min_x = x1;

		if (x1 > dc->max_x)
			dc->max_x = x1;

		if (x2 < dc->min_x)
			dc->min_x = x2;

		if (x2 > dc->max_x)
			dc->max_x = x2;

		if (y < dc->min_y)
			dc->min_y = y;

		if (y > dc->max_y)
			dc->max_y = y;
	}
	if (y < 0)
		goto gr_done;
	if (x2 < x1)
	{
		SwapI64(&x1, &x2);
		SwapI64(&z1, &z2);
	}
	if (x2 < 0)
		goto gr_done;
	if (x1 < 0)
	{
		i = -x1;
		x1 = 0;
	}
	else
		i = 0;
	j = 0;
	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		x1 += win_task->pix_left;
		x2 += win_task->pix_left;
		if (x1 > win_task->pix_right)
			goto gr_done;
		if (x2 > win_task->pix_right)
		{
			j = x2 - win_task->pix_right;
			x2 = win_task->pix_right;
		}
		y += win_task->pix_top;
		if (!(0 <= y <= win_task->pix_bottom) || x2 < 0)
			goto gr_done;
	}
	if (x1 >= dc->width || y >= dc->height)
		goto gr_done;
	dx = x2 + j - (x1 - i);
	d = dc->width_internal * y + x1;
	if (db = dc->depth_buf)
	{
		db += d;
		if (dx)
			dz = (z2 - z1) << 32 / dx;
		else
			dz = 0;
		z = z1 << 32;
	}
	if (i)
		z += i * dz;
	if (x2 >= dc->width)
		x2 = dc->width - 1;

	if (dc->flags & DCF_LOCATE_NEAREST)
	{
		if (x1 <= dc->cur_x <= x2)
			dist = 0;
		else if (dc->cur_x < x1)
			dist = SqrI64(x1 - dc->cur_x);
		else
			dist = SqrI64(dc->cur_x - x2);
		dist += SqrI64(y - dc->cur_y);
		if (dist <= dc->nearest_dist)
			dc->nearest_dist = dist;
	}
	if (dc->flags & DCF_DONT_DRAW)
		goto gr_done;

	if (	!(dc->flags & DCF_SCREEN_BITMAP) ||
			win_task->next_task == sys_winmgr_task ||
			dc->flags & DCF_ON_TOP || !IsPixCovered0(win_task, x1, y))
		char_clear = TRUE;
	else
		char_clear = FALSE;
	if (color.c1.rop & (ROPBF_DITHER | ROPBF_PROBABILITY_DITHER))
	{
		dither = TRUE;
		if (color.c1.rop & ROPBF_PROBABILITY_DITHER)
		{
			probability_dither = TRUE;
			color.c1.rop = color.c0.rop;
			dither_colors = color;
			if (RandU16 < dc->dither_probability_u16)
				color.c0 = dither_colors.c1;
			else
				color.c0 = dither_colors.c0;
		}
		else
		{
			probability_dither = FALSE;
			color.c1.rop = color.c0.rop;
			if ((x1 ^ y) & 1)
				SwapU16(&color.c0, &color.c1);
		}
	}
	else
		dither = FALSE;
	while (x1 <= x2)
	{
		if (char_clear)
		{
			if (db)
			{
				if (0 <= z.i32[1] <= *db)
				{
					*db = z.i32[1];
					plot = TRUE;
				}
				else
					plot = FALSE;
			}
			if (plot)
			{
				dst = dc->body + d;
				c = color.c0.color;
				switch [color.c0.rop]
				{
					case ROPB_EQU:
					case ROPB_MONO:
						*dst = c;
						break;

					case ROPB_COLLISION:
						c2 = *dst;
						if (c2 != TRANSPARENT && c2 != bkcolor.c0.color)
							dc->collision_count++;
						break;

					case ROPB_XOR:
						*dst ^= c;
						break;
				}
				res++;
			}
		}
		if (dither)
		{
			if (probability_dither)
			{
				if (RandU16 < dc->dither_probability_u16)
					color.c0 = dither_colors.c1;
				else
					color.c0 = dither_colors.c0;
			}
			else
				SwapU16(&color.c0, &color.c1);
		}
		d++;
		x1++;
		if (db)
			db++;
		z += dz;
		if (!(x1 & (FONT_WIDTH - 1)) && x1 <= x2)
		{
			if (	!(dc->flags & DCF_SCREEN_BITMAP)||
					win_task->next_task == sys_winmgr_task ||
					dc->flags & DCF_ON_TOP || !IsPixCovered0(win_task, x1, y))
				char_clear = TRUE;
			else
				char_clear = FALSE;
		}
	}
gr_done:
	BEqual(&Fs->task_flags, TASKf_NONTIMER_RAND, was_timer_rand);

	return res;
}

public I64 GrVLine(CDC *dc=gr.dc, I64 x, I64 y1, I64 y2, I64 z1=0, I64 z2=0)
{//3D. No transformation or thick.
//Returns count of pixs changed
	//Uses $LK,"fixed-point",A="FI:::/Demo/Lectures/FixedPoint.CC"$.
	I64				 dist, dy, dz, z, res = 0, i, j, d;
	U8				*dst;
	CColorROPU32	 c, c2, color = dc->color, bkcolor = dc->bkcolor, dither_colors;
	I32				*db;
	Bool			 plot = TRUE, char_clear, dither, probability_dither,
					 was_timer_rand = Bts(&Fs->task_flags, TASKf_NONTIMER_RAND);
	CTask			*win_task;


	if (!dc->depth_buf)
	{
		if (y2 < y1)
			SwapI64(&y1, &y2);
		return GrRect(dc, x, y1, 1, y2 - y1 + 1);
	}

	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		win_task = dc->win_task;
		x  += win_task->scroll_x;
		y1 += win_task->scroll_y;
		y2 += win_task->scroll_y;
		z1 += win_task->scroll_z;
		z2 += win_task->scroll_z;
	}
	if (dc->flags & DCF_RECORD_EXTENTS)
	{
		if (x < dc->min_x)
			dc->min_x = x;

		if (x > dc->max_x)
			dc->max_x = x;

		if (y1 < dc->min_y)
			dc->min_y = y1;

		if (y1 > dc->max_y)
			dc->max_y = y1;

		if (y2 < dc->min_y)
			dc->min_y = y2;

		if (y2 > dc->max_y)
			dc->max_y = y2;
	}
	if (x < 0)
		goto gr_done;
	if (y2 < y1)
	{
		SwapI64(&y1, &y2);
		SwapI64(&z1, &z2);
	}
	if (y2 < 0)
		goto gr_done;
	if (y1 < 0)
	{
		i = -y1;
		y1 = 0;
	}
	else
		i = 0;
	j = 0;
	if (dc->flags & DCF_SCREEN_BITMAP)
	{
		y1 += win_task->pix_top;
		y2 += win_task->pix_top;
		if (y1 > win_task->pix_bottom)
			goto gr_done;
		if (y2 > win_task->pix_bottom)
		{
			j = y2 - win_task->pix_bottom;
			y2 = win_task->pix_bottom;
		}
		x += win_task->pix_left;
		if (!(0 <= x <= win_task->pix_right) || y2 < 0)
			goto gr_done;
	}
	if (y1 >= dc->height || x >= dc->width)
		goto gr_done;
	dy = y2 + j - (y1 - i);
	d = dc->width_internal * y1 + x;
	if (db = dc->depth_buf)
	{
		db += d;
		if (dy)
			dz = (z2 - z1) << 32 / dy;
		else
			dz = 0;
		z = z1 << 32;
	}
	if (i)
		z += i * dz;
	if (y2 >= dc->height)
		y2 = dc->height - 1;

	if (dc->flags & DCF_LOCATE_NEAREST)
	{
		if (y1 <= dc->cur_y <= y2)
			dist = 0;
		else if (dc->cur_y < y1)
			dist = SqrI64(y1 - dc->cur_y);
		else
			dist = SqrI64(dc->cur_y - y2);
		dist += SqrI64(x - dc->cur_x);
		if (dist <= dc->nearest_dist)
			dc->nearest_dist = dist;
	}
	if (dc->flags & DCF_DONT_DRAW)
		goto gr_done;

	if (	!(dc->flags & DCF_SCREEN_BITMAP) ||
			win_task->next_task == sys_winmgr_task ||
			dc->flags & DCF_ON_TOP || !IsPixCovered0(win_task, x, y1))
		char_clear = TRUE;
	else
		char_clear = FALSE;
	if (color.c1.rop & (ROPBF_DITHER | ROPBF_PROBABILITY_DITHER))
	{
		dither = TRUE;
		if (color.c1.rop & ROPBF_PROBABILITY_DITHER)
		{
			probability_dither = TRUE;
			color.c1.rop = color.c0.rop;
			dither_colors = color;
			if (RandU16 < dc->dither_probability_u16)
				color.c0 = dither_colors.c1;
			else
				color.c0 = dither_colors.c0;
		}
		else
		{
			probability_dither = FALSE;
			color.c1.rop = color.c0.rop;
			if ((x ^ y1) & 1)
				SwapU16(&color.c0, &color.c1);
		}
	}
	else
		dither = FALSE;
	while (y1 <= y2)
	{
		if (char_clear)
		{
			if (db)
			{
				if (0 <= z.i32[1] <= *db)
				{
					*db = z.i32[1];
					plot = TRUE;
				}
				else
					plot = FALSE;
			}
			if (plot)
			{
				dst = dc->body + d;
				c = color.c0.color;
				switch [color.c0.rop]
				{
					case ROPB_EQU:
					case ROPB_MONO:
						*dst = c;
						break;

					case ROPB_COLLISION:
						c2 = *dst;
						if (c2 != TRANSPARENT && c2 != bkcolor.c0.color)
							dc->collision_count++;
						break;

					case ROPB_XOR:
						*dst ^= c;
						break;
				}
				res++;
			}
		}
		if (dither)
		{
			if (probability_dither)
			{
				if (RandU16 < dc->dither_probability_u16)
					color.c0 = dither_colors.c1;
				else
					color.c0 = dither_colors.c0;
			}
			else
				SwapU16(&color.c0, &color.c1);
		}
		d += dc->width_internal;
		y1++;
		if (db)
			db += dc->width_internal;
		z += dz;
		if (!(y1 & (FONT_HEIGHT - 1)) && y1 <= y2)
		{
			if (	!(dc->flags & DCF_SCREEN_BITMAP) ||
					win_task->next_task == sys_winmgr_task ||
					dc->flags & DCF_ON_TOP || !IsPixCovered0(win_task, x, y1))
				char_clear = TRUE;
			else
				char_clear = FALSE;
		}
	}
gr_done:
	BEqual(&Fs->task_flags, TASKf_NONTIMER_RAND, was_timer_rand);

	return res;
}
