#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 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 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 fixed-point.
    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 fixed-point.
    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;
}