#help_index "Graphics"

public Bool GrClamp(CDC *dc=gr.dc, I64 *left, I64 *top, I64 *right, I64 *bottom, I64 width=0, I64 height=0)
{//Returns screen, not window coordinates.
        CTask *win_task;

        *left   = 0;
        *top    = 0;
        *right  = dc->width  - 1;
        *bottom = dc->height - 1;
        if (dc->flags & DCF_SCREEN_BITMAP)
        {
                win_task = dc->win_task;
                if (GR_WIDTH - 1 < *right)
                        *right = GR_WIDTH - 1;

                if (GR_HEIGHT - 1 < *bottom)
                        *bottom = GR_HEIGHT - 1;

                if (win_task->pix_left > *left)
                        *left = win_task->pix_left;

                if (win_task->pix_top > *top)
                        *top = win_task->pix_top;

                if (win_task->pix_right < *right)
                        *right = win_task->pix_right;

                if (win_task->pix_bottom < *bottom)
                        *bottom = win_task->pix_bottom;
        }
        *left   -= width;
        *right  += width;
        *top    -= height;
        *bottom += height;

        return *left <= *right && *top <= *bottom;
}

Bool DCClipLine(CDC *dc=gr.dc, I64 *x1, I64 *y1, I64 *x2, I64 *y2, I64 width=0, I64 height=0)
{//Also converts window to screen coordinates
        I64              left, top, right, bottom;
        CTask   *win_task;

        if (GrClamp(dc, &left, &top, &right, &bottom, width, height))
        {
                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;
                }
                return ClipLine(x1, y1, x2, y2, left, top, right, bottom);
        }
        else
                return FALSE;
}

public Bool GrPlot(CDC *dc=gr.dc, I64 x, I64 y)
{//2D. Clipping but No transformation or thick.
        I32                             *db = dc->depth_buf;
        CTask                   *win_task;
        CColorROPU32     old_color;

        dc->depth_buf = NULL;
        if (dc->brush)
        {
                old_color = dc->color;
                if (dc->color.c0.rop != ROPB_COLLISION)
                        dc->color.c0.rop = ROPB_MONO;
                GrBlot(dc, x, y, dc->brush);
                dc->color = old_color;
        }
        else if (dc->flags & DCF_SCREEN_BITMAP)
        {
                win_task = dc->win_task;
                x += win_task->pix_left + win_task->scroll_x;
                y += win_task->pix_top  + win_task->scroll_y;
                if (    win_task->pix_left <= x <= win_task->pix_right  &&
                                win_task->pix_top  <= y <= win_task->pix_bottom &&
                                0 <= x < dc->width && 0 <= y < dc->height               &&
                                (win_task->next_task == sys_winmgr_task || dc->flags & DCF_ON_TOP || !IsPixCovered0(win_task, x, y)))
                        GrPlot0(dc, x, y);
        }
        else
                if (0 <= x < dc->width && 0 <= y < dc->height)
                        GrPlot0(dc, x, y);
        dc->depth_buf = db;

        return TRUE;
}

Bool GrPlot1(CDC *dc=gr.dc, I64 x, I64 y)
{//Clipping but No transformation or thick, called with db_z set
        CTask                   *win_task;
        CColorROPU32     old_color;

        if (dc->brush)
        {
                old_color = dc->color;
                if (dc->color.c0.rop != ROPB_COLLISION)
                        dc->color.c0.rop = ROPB_MONO;
                if (dc->depth_buf)
                        GrBlot3(dc, x, y, dc->db_z, dc->brush);
                else
                        GrBlot(dc, x, y, dc->brush);
                dc->color = old_color;
        }
        else if (dc->flags & DCF_SCREEN_BITMAP)
        {
                win_task = dc->win_task;
                x += win_task->pix_left + win_task->scroll_x;
                y += win_task->pix_top  + win_task->scroll_y;
                if (    win_task->pix_left <= x <= win_task->pix_right  &&
                                win_task->pix_top  <= y <= win_task->pix_bottom &&
                                0 <= x<dc->width && 0 <= y<dc->height                   &&
                                (win_task->next_task == sys_winmgr_task || dc->flags & DCF_ON_TOP || !IsPixCovered0(win_task, x, y)))
                        GrPlot0(dc, x, y);
        }
        else
                if (0 <= x < dc->width && 0 <= y < dc->height)
                        GrPlot0(dc, x, y);

        return TRUE;
}

public I64 GrPeek(CDC *dc=gr.dc, I64 x, I64 y)
{//2D. Clipping but no transformation.
//Returns pix color or -1 if off-screen or covered.
        CTask *win_task;

        if (dc->flags & DCF_SCREEN_BITMAP)
        {
                win_task = dc->win_task;
                x += win_task->pix_left + win_task->scroll_x;
                y += win_task->pix_top  + win_task->scroll_y;
                if (    !(win_task->pix_left <= x <= win_task->pix_right)       ||
                                !(win_task->pix_top  <= y <= win_task->pix_bottom)      ||
                                !(0 <= x < dc->width) || !(0 <= y < dc->height)         ||
                                win_task->next_task != sys_winmgr_task && !(dc->flags & DCF_ON_TOP) && IsPixCovered0(win_task, x, y))
                        return -1;
        }
        else
                if (!(0 <= x<dc->width) || !(0 <= y<dc->height))
                        return -1;

        return GrPeek0(dc, x, y);
}

/*

This is an easier to understand
version of the nonrecursive routine below.
I64 GrFloodFillRay(CDC *dc, I64 x, I64 y, I64 z, I32 *db)
{
        I64 res, j, x1, ray_len, ray_len2;

        if (UnusedStack < 0x80)
                Panic("Stack Overflow", Fs);

        res = ray_len = GrRayLen(dc, &x, y, z, db);
        y--;
        j = ray_len;
        x1 = x;
        while (j > 0)
        {
                if (ray_len2 = GrRayLenMinus(dc, x1, y))
                        res += GrFloodFillRay(dc, x1, y, z, db);
                j  -= ray_len2 + 1;
                x1 -= ray_len2 + 1;
        }
        y += 2;
        j = ray_len;
        x1 = x;
        while (j > 0)
        {
                if (ray_len2 = GrRayLenMinus(dc, x1, y))
                        res += GrFloodFillRay(dc, x1, y, z, db);
                j  -= ray_len2 + 1;
                x1 -= ray_len2 + 1;
        }

        return res;
}
*/

class CFFRay
{
        I64 state, x, y, j, x1, ray_len, ray_len2;
};

I64 GrFloodFillRay(CDC *dc, I64 x, I64 y, I64 z, I32 *db)
{//See the above commented-out routine for an easier to understand version.
//Returns count of pixs changed
        I64              res = 0;
//We don't dynamically calculate the size to avoid fragmentation of memory.
        CFFRay  *f_dc = MAlloc(sizeof(CFFRay) * 0x80000), *f = f_dc;

        f->x     = x;
        f->y     = y;
        f->state = 0;
        do
        {
                switch [f->state]
                {
                        case 0:
                                f->state++;
                                res += f->ray_len = GrRayLen(dc, &f->x, f->y, z, db);
                                f->y--;
                                f->j  = f->ray_len;
                                f->x1 = f->x;
                                break;

                        case 1:
                                if (f->j > 0)
                                {
                                        f->state++;
                                        if (f->ray_len2 = GrRayLenMinus(dc, f->x1, f->y))
                                        {
                                                f[1].x          = f->x1;
                                                f[1].y          = f->y;
                                                f[1].state      = 0;
                                                f++;
                                        }
                                }
                                else
                                        f->state += 2;
                                break;

                        case 2:
                                f->state--;
                                f->j  -= f->ray_len2 + 1;
                                f->x1 -= f->ray_len2 + 1;
                                break;

                        case 3:
                                f->state++;
                                f->y += 2;
                                f->j  = f->ray_len;
                                f->x1 = f->x;
                                break;

                        case 4:
                                if (f->j > 0)
                                {
                                        f->state++;
                                        if (f->ray_len2 = GrRayLenMinus(dc, f->x1, f->y))
                                        {
                                                f[1].x          = f->x1;
                                                f[1].y          = f->y;
                                                f[1].state      = 0;
                                                f++;
                                        }
                                }
                                else
                                        f->state += 2;
                                break;

                        case 5:
                                f->state--;
                                f->j  -= f->ray_len2 + 1;
                                f->x1 -= f->ray_len2 + 1;
                                break;

                        case 6:
                                f--;
                                break;
                }
        }
        while (f >= f_dc);

        Free(f_dc);

        return res;
}

public I64 GrFloodFill(CDC *dc=gr.dc, I64 x, I64 y, Bool not_color=FALSE, I64 z=0, I32 *db=NULL)
{//2D. Ignore z and db.
//not_color=TRUE means fill up to everything which is not the current color.
        //not_color=FALSE means fill all parts equ to the color under the point.
        //Returns count of pixs changed
        I64                              res = 0, j, old_flags = dc->flags;
        CColorROPU32     old_color2 = dc->color2;
        CDC                             *old_brush;

        if (dc->flags & DCF_DONT_DRAW) //TODO
                return 0;
        old_brush = dc->brush;
        dc->brush = NULL;
        if ((j = GrPeek(dc, x, y)) >= 0)
        {
                if (not_color)
                {
                        dc->color2 = dc->color.c0.color;
                        dc->flags |= DCF_FILL_NOT_COLOR;
                }
                else
                {
                        dc->color2 = j;
                        if (dc->color.c1.rop & ROPBF_DITHER)
                        {
                                if (dc->color2.c0.color == dc->color.c0.color && dc->color.c0.color == dc->color.c1.color)
                                        goto ff_done;
                        }
                        else if (dc->color2.c0.color == dc->color.c0.color)
                                goto ff_done;
                        dc->flags &= ~DCF_FILL_NOT_COLOR;
                }
                if (not_color && j != dc->color2 || !not_color)
                        res = GrFloodFillRay(dc, x, y, z, db);
        }
ff_done:
        dc->brush       = old_brush;
        dc->flags       = old_flags;
        dc->color2      = old_color2;

        return res;
}

I64 GrFillSemiCircle(CDC *dc=gr.dc, I64 cx, I64 cy, I64 z=0, I64 diameter, I64 n)
{//2D. Clipping but not transformation.
        I64 res = 0, i, k, r = diameter >> 1, rr;

        if (diameter >= 1)
                switch (n)
                {
                        case 0:
                                if (diameter < GR_PEN_BRUSHES_NUM)
                                        for (i = 0; i < r; i++)
                                                res += GrHLine(dc, gr.circle_lo[diameter][i] + cx, gr.circle_hi[diameter][i] + cx, cy + i - r, z, z);
                                else
                                {
                                        k = diameter + 1;
                                        rr = SqrI64((k + 1) >> 1);
                                        for (i = 0; i < r; i++)
                                                res += GrHLine(dc, -Sqrt(rr - SqrI64(r - i)) + cx, Sqrt(rr - SqrI64(r - i)) + cx, cy + i - r, z, z);
                                }
                                break;

                        case 1:
                                if (diameter < GR_PEN_BRUSHES_NUM)
                                        for (i = r + 1 ; i < diameter; i++)
                                                res += GrHLine(dc, gr.circle_lo[diameter][i] + cx, gr.circle_hi[diameter][i] + cx, cy + i - r, z, z);
                                else
                                {
                                        k = diameter + 1;
                                        rr = SqrI64((k + 1) >> 1);
                                        for (i = r + 1; i < k; i++)
                                                res += GrHLine(dc, -Sqrt(rr - SqrI64(i - r)) + cx, Sqrt(rr - SqrI64(i - r)) + cx, cy + i - r, z, z);
                                }
                                break;

                        case 2:
                                if (diameter < GR_PEN_BRUSHES_NUM)
                                        for (i = 0; i < r; i++)
                                                res += GrVLine(dc, cx + i - r, gr.circle_lo[diameter][i] + cy, gr.circle_hi[diameter][i] + cy, z, z);
                                else
                                {
                                        k = diameter + 1;
                                        rr = SqrI64((k + 1) >> 1);
                                        for (i = 0; i < r; i++)
                                                res += GrVLine(dc, cx + i - r, -Sqrt(rr - SqrI64(r - i)) + cy, Sqrt(rr - SqrI64(r - i)) + cy, z, z);
                                }
                                break;

                        case 3:
                                if (diameter < GR_PEN_BRUSHES_NUM)
                                        for (i = r + 1; i < diameter; i++)
                                                res += GrVLine(dc, cx + i - r, gr.circle_lo[diameter][i] + cy, gr.circle_hi[diameter][i] + cy, z, z);
                                else
                                {
                                        k = diameter + 1;
                                        rr = SqrI64((k + 1) >> 1);
                                        for (i = r + 1; i < k; i++)
                                                res += GrVLine(dc, cx + i - r, -Sqrt(rr - SqrI64(i - r)) + cy, Sqrt(rr - SqrI64(i - r)) + cy, z, z);
                                }
                                break;

                        case 4:
                                if (diameter < GR_PEN_BRUSHES_NUM)
                                        for (i = 0; i < r; i++)
                                                res += GrHLine(dc, gr.circle_lo[diameter][i] + cx, gr.circle_hi[diameter][i] + cx, cy + i - r, z, z);
                                else
                                {
                                        k = diameter + 1;
                                        rr = SqrI64((k + 1) >> 1);
                                        for (i = 0; i < r; i++)
                                                res += GrHLine(dc, -Sqrt(rr - SqrI64(r - i)) + cx, Sqrt(rr - SqrI64(r - i)) + cx, cy + i - r, z, z);
                                }
                                break;

                        case 5:
                                if (diameter < GR_PEN_BRUSHES_NUM)
                                        for (i = r + 1; i < diameter; i++)
                                                res += GrHLine(dc, gr.circle_lo[diameter][i] + cx, gr.circle_hi[diameter][i] + cx, cy + i - r, z, z);
                                else
                                {
                                        k = diameter + 1;
                                        rr = SqrI64((k + 1) >> 1);
                                        for (i = r + 1; i < k; i++)
                                                res += GrHLine(dc, -Sqrt(rr - SqrI64(i - r)) + cx, Sqrt(rr - SqrI64(i - r)) + cx, cy + i - r, z, z);
                                }
                                break;

                        case 6:
                                if (diameter < GR_PEN_BRUSHES_NUM)
                                        for (i = 0; i < r; i++)
                                                res += GrVLine(dc, cx + i - r, gr.circle_lo[diameter][i] + cy, gr.circle_hi[diameter][i] + cy, z, z);
                                else
                                {
                                        k = diameter + 1;
                                        rr = SqrI64((k + 1) >> 1);
                                        for (i = 0; i < r; i++)
                                                res += GrVLine(dc, cx + i - r, -Sqrt(rr - SqrI64(r - i)) + cy, Sqrt(rr - SqrI64(r - i)) + cy, z, z);
                                }
                                break;

                        case 7:
                                if (diameter < GR_PEN_BRUSHES_NUM)
                                        for (i = r + 1; i < diameter; i++)
                                                res += GrVLine(dc, cx + i - r, gr.circle_lo[diameter][i] + cy, gr.circle_hi[diameter][i] + cy, z, z);
                                else
                                {
                                        k = diameter + 1;
                                        rr = SqrI64((k + 1) >> 1);
                                        for (i = r + 1; i < k; i++)
                                                res += GrVLine(dc, cx + i - r, -Sqrt(rr - SqrI64(i - r)) + cy, Sqrt(rr - SqrI64(i - r)) + cy, z, z);
                                }
                                break;
                }

        return res;
}

public I64 GrFillCircle(CDC *dc=gr.dc, I64 cx, I64 cy, I64 z=0, I64 diameter)
{//2D. Clipping but not transformation.
        I64 res = 0, i, k, r = diameter >> 1, rr;

        if (diameter >= 1)
        {
                if (diameter < GR_PEN_BRUSHES_NUM)
                        for (i = 0; i < diameter; i++)
                                res += GrHLine(dc, gr.circle_lo[diameter][i] + cx, gr.circle_hi[diameter][i] + cx, cy + i - r, z, z);
                else
                {
                        k = diameter + 1;
                        rr = SqrI64((k + 1) >> 1);
                        for (i = 0; i <= r; i++)
                                res += GrHLine(dc, -Sqrt(rr - SqrI64(r - i)) + cx, Sqrt(rr - SqrI64(r - i)) + cx, cy + i - r, z, z);
                        for (; i < k; i++)
                                res += GrHLine(dc, -Sqrt(rr - SqrI64(i - r)) + cx, Sqrt(rr - SqrI64(i - r)) + cx, cy + i - r, z, z);
                }
        }

        return res;
}

public Bool GrPlot3B(CDC *dc=gr.dc, I64 x, I64 y, I64 z)
{//3D. Clipping and transformation but no thick.
        I64  _x, _y, _z;
        Bool was_transform = FALSE, was_symmetry = FALSE;

        if (dc->flags & DCF_TRANSFORMATION)
        {
                (*dc->transform)(dc, &x, &y, &z);
                dc->flags &= ~DCF_TRANSFORMATION;
                was_transform = TRUE;
        }
        if (dc->flags & DCF_SYMMETRY)
        {
                _x = x;
                _y = y;
                _z = z;
                DCReflect(dc, &_x, &_y, &_z);
                dc->flags &= ~DCF_SYMMETRY;
                dc->db_z = _z;
                GrPlot1(dc, _x, _y);
                was_symmetry = TRUE;
                if (dc->flags & DCF_JUST_MIRROR)
                        goto gr_done;
        }
        dc->db_z = z;
        GrPlot1(dc, x, y);
gr_done:
        if (was_transform)
                dc->flags |= DCF_TRANSFORMATION;
        if (was_symmetry)
                dc->flags |= DCF_SYMMETRY;

        return TRUE;
}

public Bool GrPlot3(CDC *dc=gr.dc, I64 x, I64 y, I64 z)
{//3D. Clipping and transformation and thick.
        I64                              _x, _y, _z, w, dist;
        CColorROPU32     old_color = dc->color;
        Bool                     record, was_transform = FALSE, was_symmetry = FALSE;
        CTask                   *win_task;

        if (dc->flags & DCF_TRANSFORMATION)
        {
                (*dc->transform)(dc, &x, &y, &z);
                dc->flags &= ~DCF_TRANSFORMATION;
                was_transform = TRUE;
        }
        if (dc->flags & DCF_SYMMETRY)
        {
                _x = x;
                _y = y;
                _z = z;
                DCReflect(dc, &_x, &_y, &_z);
                dc->flags &= ~DCF_SYMMETRY;
                GrPlot3(dc, _x, _y, _z);
                was_symmetry = TRUE;
                if (dc->flags & DCF_JUST_MIRROR)
                        goto gr_done;
        }
        w = dc->thick >> 1;
        dc->db_z = z;
        if (dc->brush || w <= 0)
                GrPlot1(dc, x, y);
        else if (dc->thick < GR_PEN_BRUSHES_NUM)
        {
                if (dc->color.c0.rop != ROPB_COLLISION)
                        dc->color.c0.rop = ROPB_MONO;
                if (dc->depth_buf)
                {
                        if (dc->color.c1.rop & ROPBF_DITHER)
                        {
                                dc->color.c1.rop = dc->color.c0.rop;
                                if (((x - w) ^ (y - w)) & 1)
                                {
                                        record = GrBlot3(dc, x - w, y - w, z, gr.odd_pen_brushes[dc->thick]);
                                        dc->color.c0 = dc->color.c1;
                                        record = GrBlot3(dc, x - w, y - w, z, gr.even_pen_brushes[dc->thick]);
                                }
                                else
                                {
                                        record = GrBlot3(dc, x - w, y - w, z, gr.even_pen_brushes[dc->thick]);
                                        dc->color.c0 = dc->color.c1;
                                        record = GrBlot3(dc, x - w, y - w, z, gr.odd_pen_brushes[dc->thick]);
                                }
                        }
                        else
                        {
                                if (dc->color.c0.rop == ROPB_COLLISION)
                                {
                                        if (dc->color.c0.color != dc->bkcolor.c0.color && dc->color.c0.color != TRANSPARENT)
                                                record=GrBlot3(dc, x - w, y - w, z, gr.collision_pen_brushes[dc->thick]);
                                        else
                                                record = FALSE;
                                }
                                else
                                        record = GrBlot3(dc, x - w, y - w, z, gr.pen_brushes[dc->thick]);
                        }
                }
                else
                {
                        if (dc->color.c1.rop & ROPBF_DITHER)
                        {
                                dc->color.c1.rop = dc->color.c0.rop;
                                if (((x - w) ^ (y - w)) & 1)
                                {
                                        record = GrBlot(dc, x - w, y - w, gr.odd_pen_brushes[dc->thick]);
                                        dc->color.c0 = dc->color.c1;
                                        record = GrBlot(dc, x - w, y - w, gr.even_pen_brushes[dc->thick]);
                                }
                                else
                                {
                                        record = GrBlot(dc, x - w, y - w, gr.even_pen_brushes[dc->thick]);
                                        dc->color.c0 = dc->color.c1;
                                        record = GrBlot(dc, x - w, y - w, gr.odd_pen_brushes[dc->thick]);
                                }
                        }
                        else
                        {
                                if (dc->color.c0.rop == ROPB_COLLISION)
                                {
                                        if (dc->color.c0.color != dc->bkcolor.c0.color && dc->color.c0.color != TRANSPARENT)
                                                record = GrBlot(dc, x - w, y - w, gr.collision_pen_brushes[dc->thick]);
                                        else
                                                record = FALSE;
                                }
                                else
                                        record = GrBlot(dc, x - w, y - w, gr.pen_brushes[dc->thick]);
                        }
                }
                if (record)
                {
                        if (dc->flags & DCF_SCREEN_BITMAP)
                        {
                                win_task = dc->win_task;
                                x += win_task->pix_left + win_task->scroll_x;
                                y += win_task->pix_top  + win_task->scroll_y;
                        }
                        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 - w < dc->min_x)
                                        dc->min_x = x - w;
                                if (y - w < dc->min_y)
                                        dc->min_y = y - w;
                                if (dc->thick & 1)
                                {
                                        if (x + w > dc->max_x)
                                                dc->max_x = x + w;
                                        if (y + w > dc->max_y)
                                                dc->max_y = y + w;
                                }
                                else
                                {
                                        if (x + w - 1 > dc->max_x)
                                                dc->max_x = x + w - 1;
                                        if (y + w - 1 > dc->max_y)
                                                dc->max_y = y + w - 1;
                                }
                        }
                }
        }
        else
                GrFillCircle(dc, x, y, dc->db_z, dc->thick);
gr_done:
        dc->color = old_color;
        if (was_transform)
                dc->flags |= DCF_TRANSFORMATION;
        if (was_symmetry)
                dc->flags |= DCF_SYMMETRY;

        return TRUE;
}

Bool GrLinePlot0(CDC *dc, I64 x, I64 y, I64 z)
{//This is a callback.
        CTask *win_task = dc->win_task;

        if (!(dc->flags & DCF_SCREEN_BITMAP)            ||
                win_task->next_task == sys_winmgr_task  ||
                dc->flags & DCF_ON_TOP                                  ||
                !IsPixCovered0(win_task, x, y))
        {
                dc->db_z = z;
                GrPlot0(dc, x, y);
        }

        return TRUE;
}

Bool GrLinePlot(CDC *dc, I64 x, I64 y, I64 z)
{//This is a callback.
        dc->db_z = z;
        GrPlot1(dc, x, y);

        return TRUE;
}

public Bool GrLine(CDC *dc=gr.dc, I64 x1, I64 y1, I64 x2, I64 y2, I64 step=1, I64 start=0)
{//2D. Clipping but not transformation.
        Bool res = FALSE;
        I32 *db = dc->depth_buf;

        dc->depth_buf = NULL;
        if (step == 1 && !start && !dc->brush && !dc->depth_buf)
        {
                if (DCClipLine(dc, &x1, &y1, &x2, &y2))
                        res = Line(dc, x1, y1, 0, x2, y2, 0, &GrLinePlot0, step, start);
        }
        else
                res = Line(dc, x1, y1, 0, x2, y2, 0, &GrLinePlot, step, start);
        dc->depth_buf = db;

        return res;
}

public Bool GrCircle(CDC *dc=gr.dc, I64 cx, I64 cy, I64 radius, I64 step=1, F64 start_radians=0, F64 len_radians=2*pi)
{//2D. Clipping but not transformation.
        Bool res;
        I32 *db = dc->depth_buf;

        dc->depth_buf = NULL;
        res = Circle(dc, cx, cy, 0, radius, &GrLinePlot, step, start_radians, len_radians);
        dc->depth_buf = db;

        return res;
}

public Bool GrEllipse(CDC *dc=gr.dc, I64 cx, I64 cy, I64 x_radius, I64 y_radius, 
                                          F64 rot_angle=0, I64 step=1, F64 start_radians=0, F64 len_radians=2*pi)
{//2D. Clipping but not transformation.
        Bool res;
        I32 *db = dc->depth_buf;

        dc->depth_buf = NULL;
        res = Ellipse(dc, cx, cy, 0, x_radius, y_radius, &GrLinePlot, rot_angle, step, start_radians, len_radians);
        dc->depth_buf = db;

        return res;
}

public Bool GrRegPoly(CDC *dc=gr.dc, I64 cx, I64 cy, I64 x_radius, I64 y_radius, I64 sides, F64 rot_angle=0, 
                                          I64 step=1, F64 start_radians=0, F64 len_radians=2*pi)
{//2D. Clipping but no transform or thick.
        Bool res;
        I32 *db = dc->depth_buf;

        dc->depth_buf = NULL;
        res = RegPoly(dc, cx, cy, 0, x_radius, y_radius, sides, &GrLinePlot, rot_angle, step, start_radians, len_radians);
        dc->depth_buf = db;

        return res;
}

public Bool Gr2Bezier(CDC *dc=gr.dc, CD3I32 *ctrl)
{//2nd order. Clipping but no transform or thick.
        return Bezier2(dc, ctrl, &GrLinePlot);
}

public Bool Gr3Bezier(CDC *dc=gr.dc, CD3I32 *ctrl)
{//3rd order. Clipping but no transform or thick.
        return Bezier3(dc, ctrl, &GrLinePlot);
}

public Bool Gr2BSpline(CDC *dc=gr.dc, CD3I32 *ctrl, I64 count, Bool closed=FALSE)
{//2nd order. Clipping but no transform or thick.
        return BSpline2(dc, ctrl, count, &GrLinePlot, closed);
}

public Bool Gr3BSpline(CDC *dc=gr.dc, CD3I32 *ctrl, I64 count, Bool closed=FALSE)
{//3rd order. Clipping but no transform or thick.
        return BSpline3(dc, ctrl, count, &GrLinePlot, closed);
}

I64 GrLineFat3(CDC *dc=gr.dc, I64 x1, I64 y1, I64 z1, I64 x2, I64 y2, I64 z2, I64 width, I64 start=0)
{//Step through line segment calling callback.
//Uses fixed-point.
        I64 res = 0, i, j, d, dx = x2 - x1, dy = y2 - y1, dz = z2 - z1, _x, _y, _z, d_lo, d_hi,
                adx = AbsI64(dx), ady = AbsI64(dy), adz = AbsI64(dz);

        if (width > 0)
        {
                if (adx >= ady)
                {
                        if (adx >= adz)
                        {
                                if (d = adx)
                                {
                                        if (dx >= 0)
                                                dx = 0x100000000;
                                        else
                                                dx = -0x100000000;
                                        dy = dy << 32 / d;
                                        dz = dz << 32 / d;
                                }
                        }
                        else
                        {
                                if (d = adz)
                                {
                                        dx = dx << 32 / d;
                                        dy = dy << 32 / d;
                                        if (dz >= 0)
                                                dz = 0x100000000;
                                        else
                                                dz = -0x100000000;
                                }
                        }
                        x1 <<= 32;
                        y1 <<= 32;
                        z1 <<= 32;
                        for (j = 0; j < start; j++)
                        {
                                x1 += dx;
                                y1 += dy;
                                z1 += dz;
                        }
                        if (start >= d)
                                res += GrFillCircle(dc, x1.i32[1], y1.i32[1], z1.i32[1], width);
                        else
                        {
                                if (width == 1)
                                        for (i = start; i <= d; i++)
                                        {
                                                dc->db_z = z1.i32[1];
                                                res += GrPlot1(dc, x1.i32[1], y1.i32[1]);
                                                _x = x1.i32[1];
                                                _y = y1.i32[1];
                                                _z = z1.i32[1];
                                                x1 += dx;
                                                y1 += dy;
                                                z1 += dz;
                                        }
                                else
                                {
                                        i = width * Sqrt(SqrI64(adx) + SqrI64(ady)) / adx;
                                        d_lo = i >> 1;
                                        d_hi = (i - 1) >> 1;

                                        if (dx >= 0)
                                                res += GrFillSemiCircle(dc, x1.i32[1], y1.i32[1], z1.i32[1], width, 2);
                                        else
                                                res += GrFillSemiCircle(dc, x1.i32[1], y1.i32[1], z1.i32[1], width, 7);
                                        for (i = start; i <= d; i++)
                                        {
                                                res += GrVLine(dc, x1.i32[1], y1.i32[1] - d_lo, y1.i32[1] + d_hi, z1.i32[1], z1.i32[1]);
                                                _x = x1.i32[1];
                                                _y = y1.i32[1];
                                                _z = z1.i32[1];
                                                x1 += dx;
                                                y1 += dy;
                                                z1 += dz;
                                        }
                                        x1 -= dx;
                                        y1 -= dy;
                                        z1 -= dz;
                                        if (dx >= 0)
                                                res += GrFillSemiCircle(dc, x1.i32[1], y1.i32[1], z1.i32[1], width, 3);
                                        else
                                                res += GrFillSemiCircle(dc, x1.i32[1], y1.i32[1], z1.i32[1], width, 6);
                                }
                        }
                }
                else
                {
                        if (ady >= adz)
                        {
                                if (d = ady)
                                {
                                        dx = dx << 32 / d;
                                        if (dy >= 0)
                                                dy = 0x100000000;
                                        else
                                                dy = -0x100000000;
                                        dz = dz << 32 / d;
                                }
                        }
                        else
                        {
                                if (d = adz)
                                {
                                        dx = dx << 32 / d;
                                        dy = dy << 32 / d;
                                        if (dz >= 0)
                                                dz = 0x100000000;
                                        else
                                                dz = -0x100000000;
                                }
                        }
                        x1 <<= 32;
                        y1 <<= 32;
                        z1 <<= 32;
                        for (j = 0; j < start; j++)
                        {
                                x1 += dx;
                                y1 += dy;
                                z1 += dz;
                        }
                        if (start >= d)
                                res += GrFillCircle(dc, x1.i32[1], y1.i32[1], z1.i32[1], width);
                        else
                        {
                                if (width == 1)
                                        for (i = start; i <= d; i++)
                                        {
                                                dc->db_z = z1.i32[1];
                                                res += GrPlot1(dc, x1.i32[1], y1.i32[1]);
                                                _x = x1.i32[1];
                                                _y = y1.i32[1];
                                                _z = z1.i32[1];
                                                x1 += dx;
                                                y1 += dy;
                                                z1 += dz;
                                        }
                                else
                                {
                                        i = width * Sqrt(SqrI64(ady) + SqrI64(adx)) / ady;
                                        d_lo = i >> 1;
                                        d_hi = (i - 1) >> 1;

                                        if (dy >= 0)
                                                res += GrFillSemiCircle(dc, x1.i32[1], y1.i32[1], z1.i32[1], width, 0);
                                        else
                                                res += GrFillSemiCircle(dc, x1.i32[1], y1.i32[1], z1.i32[1], width, 5);
                                        for (i = start; i <= d; i++)
                                        {
                                                res += GrHLine(dc, x1.i32[1] - d_lo, x1.i32[1] + d_hi, y1.i32[1], z1.i32[1], z1.i32[1]);
                                                _x = x1.i32[1];
                                                _y = y1.i32[1];
                                                _z = z1.i32[1];
                                                x1 += dx;
                                                y1 += dy;
                                                z1 += dz;
                                        }
                                        x1 -= dx;
                                        y1 -= dy;
                                        z1 -= dz;
                                        if (dy >= 0)
                                                res += GrFillSemiCircle(dc, x1.i32[1], y1.i32[1], z1.i32[1], width, 1);
                                        else
                                                res += GrFillSemiCircle(dc, x1.i32[1], y1.i32[1], z1.i32[1], width, 4);
                                }
                        }
                }
        }

        return res;
}

public Bool GrLine3(CDC *dc=gr.dc, I64 x1, I64 y1, I64 z1, I64 x2, I64 y2, I64 z2, I64 step=1, I64 start=0)
{//3D. Transformation with thick.
        I64  _x1, _y1, _z1, _x2, _y2, _z2;
        Bool res = FALSE, was_transform = FALSE, was_symmetry = FALSE;

        if (dc->flags & DCF_TRANSFORMATION)
        {
                (*dc->transform)(dc, &x1, &y1, &z1);
                (*dc->transform)(dc, &x2, &y2, &z2);
                dc->flags &= ~DCF_TRANSFORMATION;
                was_transform = TRUE;
        }
        if (dc->flags & DCF_SYMMETRY)
        {
                _x1 = x1;
                _y1 = y1;
                _z1 = z1;
                DCReflect(dc, &_x1, &_y1, &_z1);
                _x2 = x2;
                _y2 = y2;
                _z2 = z2;
                DCReflect(dc, &_x2, &_y2, &_z2);
                dc->flags &= ~DCF_SYMMETRY;
                if (step == 1 && !dc->brush)
                {
                        if (!start && dc->thick < 2 && !dc->depth_buf)
                        {//TODO: clip z depbuf
                                if (DCClipLine(dc, &_x1, &_y1, &_x2, &_y2))
                                        res = Line(dc, _x1, _y1, 0, _x2, _y2, 0, &GrLinePlot0, step, start);
                        }
                        else
                        {
                                if (GrLineFat3(dc, _x1, _y1, _z1, _x2, _y2, _z2, dc->thick, start))
                                        res = TRUE;
                        }
                }
                else
                        res = Line(dc, _x1, _y1, _z1, _x2, _y2, _z2, &GrPlot3, step, start);
                was_symmetry = TRUE;
                if (dc->flags & DCF_JUST_MIRROR)
                        goto gr_done;
        }
        if (step == 1 && !dc->brush)
        {
                if (!start && dc->thick < 2 && !dc->depth_buf)
                {//TODO: clip z depbuf
                        if (DCClipLine(dc, &x1, &y1, &x2, &y2))
                                res |= Line(dc, x1, y1, 0, x2, y2, 0, &GrLinePlot0, step, start);
                }
                else
                {
                        if (GrLineFat3(dc, x1, y1, z1, x2, y2, z2, dc->thick, start))
                                res = TRUE;
                }
        }
        else
                res |= Line(dc, x1, y1, z1, x2, y2, z2, &GrPlot3, step, start);
gr_done:
        if (was_transform)
                dc->flags |= DCF_TRANSFORMATION;
        if (was_symmetry)
                dc->flags |= DCF_SYMMETRY;

        return res;
}

#help_index "Graphics/Char;Char/Graphics"

public Bool GrPutChar3(CDC *dc=gr.dc, I64 x, I64 y, I64 z, U8 ch)
{//3D. Transformation. DCF_SYMMETRY is silly.
        if (dc->flags & DCF_TRANSFORMATION)
                (*dc->transform)(dc, &x, &y, &z);

        return GrPutChar(dc, x, y, ch);
}

public I64 GrPrint3(CDC *dc=gr.dc, I64 x, I64 y, I64 z, U8 *format, ...)
{//3D. Transformation. DCF_SYMMETRY is silly.
        U8 *buf = StrPrintJoin(NULL, format, argc, argv);
        I64 res;

        if (dc->flags & DCF_TRANSFORMATION)
                (*dc->transform)(dc, &x, &y, &z);
        res = GrPrint(dc, x, y, "%s", buf);
        Free(buf);

        return res;
}

public I64 GrVPrint3(CDC *dc=gr.dc, I64 x, I64 y, I64 z, U8 *format, ...)
{//3D. Vertical text. Transformation. DCF_SYMMETRY is silly.
        U8 *buf = StrPrintJoin(NULL, format, argc, argv);
        I64 res;

        if (dc->flags & DCF_TRANSFORMATION)
                (*dc->transform)(dc, &x, &y, &z);
        res = GrVPrint(dc, x, y, "%s", buf);
        Free(buf);

        return res;
}

#help_index "Graphics"

public Bool GrEllipse3(CDC *dc=gr.dc, I64 cx, I64 cy, I64 cz, I64 x_radius, I64 y_radius, F64 rot_angle=0, 
                                           I64 step=1, F64 start_radians=0, F64 len_radians=2*pi)
{//3D. Transformation with thick.
        Bool res;
        I64  x, y, z, xx, yy, zz;
        F64  m1, arg1, m2, arg2, s, c;

        if (dc->flags & DCF_TRANSFORMATION)
        {
                dc->flags &= ~DCF_TRANSFORMATION;
                (*dc->transform)(dc, &cx, &cy, &cz);

                c = Cos(rot_angle);
                s = Sin(rot_angle);

                x_radius <<= 16;
                y_radius <<= 16;

                xx = 0;
                yy = 0;
                zz = 0;
                (*dc->transform)(dc, &xx, &yy, &zz);

                x = x_radius * c;
                y = x_radius * s;
                z = 0;
                (*dc->transform)(dc, &x, &y, &z);
                x -= xx;
                y -= yy;
                z -= zz;
                R2P(&m1, &arg1, x, y);

                x = -y_radius * s;
                y = y_radius * c;
                z = 0;
                (*dc->transform)(dc, &x, &y, &z);
                x -= xx;
                y -= yy;
                z -= zz;
                R2P(&m2, &arg2, x, y);
                m2 *= Abs(Sin(arg2 - arg1));

                res = Ellipse(dc, cx, cy, cz, m1 / 0x10000, m2 / 0x10000, &GrPlot3, -arg1, step, start_radians, len_radians);
                dc->flags |= DCF_TRANSFORMATION;
        }
        else
                res = Ellipse(dc, cx, cy, cz, x_radius, y_radius, &GrPlot3, rot_angle, step, start_radians, len_radians);

        return res;
}

public Bool GrCircle3(CDC *dc=gr.dc, I64 cx, I64 cy, I64 cz, I64 radius, I64 step=1, F64 start_radians=0, F64 len_radians=2*pi)
{//3D. Transformation with thick.

        if (dc->flags & DCF_TRANSFORMATION)
                return GrEllipse3(dc, cx, cy, cz, radius, radius, 0, step, start_radians, len_radians);
        else
                return Circle(dc, cx, cy, cz, radius, &GrPlot3, step, start_radians, len_radians);
}

public Bool GrRegPoly3(CDC *dc=gr.dc, I64 cx, I64 cy, I64 cz, I64 x_radius, I64 y_radius, I64 sides, F64 rot_angle=0, 
                                           I64 step=1, F64 start_radians=0, F64 len_radians=2*pi)
{//3D. Clipping and transform and thick.
        Bool res;
        I64  x, y, z, xx, yy, zz;
        F64  m1, arg1, m2, arg2, s, c;

        if (dc->flags & DCF_TRANSFORMATION)
        {
                dc->flags &= ~DCF_TRANSFORMATION;
                (*dc->transform)(dc, &cx, &cy, &cz);

                c = Cos(rot_angle);
                s = Sin(rot_angle);

                x_radius <<= 16;
                y_radius <<= 16;

                xx = 0;
                yy = 0;
                zz = 0;
                (*dc->transform)(dc, &xx, &yy, &zz);

                x = x_radius * c;
                y = x_radius * s;
                z = 0;
                (*dc->transform)(dc, &x, &y, &z);
                x -= xx;
                y -= yy;
                z -= zz;
                R2P(&m1, &arg1, x, y);

                x = -y_radius * s;
                y = y_radius * c;
                z = 0;
                (*dc->transform)(dc, &x, &y, &z);
                x -= xx;
                y -= yy;
                z -= zz;
                R2P(&m2, &arg2, x, y);
                m2 *= Abs(Sin(arg2 - arg1));

                res = RegPoly(dc, cx, cy, cz, m1 / 0x10000, m2 / 0x10000, sides, &GrPlot3, -arg1, step, start_radians, len_radians);
                dc->flags |= DCF_TRANSFORMATION;
        }
        else
                res = RegPoly(dc, cx, cy, cz, x_radius, y_radius, sides, &GrPlot3, rot_angle, step, start_radians, len_radians);

        return res;
}

public I64 GrFloodFill3(CDC *dc=gr.dc, I64 x1, I64 y1, I64 z1, Bool not_color=FALSE)
{//3D. Transformation.
//not_color=TRUE means fill up to everything which is not the current color.
        //not_color=FALSE means fill all parts equ to the color under the point.
        //Returns count of pixs changed
        I64 res, old_flags = dc->flags, _x, _y, _z;

        if (dc->flags & DCF_TRANSFORMATION)
        {
                (*dc->transform)(dc, &x1, &y1, &z1);
                dc->flags &= ~DCF_TRANSFORMATION;
        }
        if (dc->flags & DCF_SYMMETRY)
        {
                _x = x1;
                _y = y1;
                _z = z1;
                DCReflect(dc, &_x, &_y, &_z);
                dc->flags &= ~DCF_SYMMETRY;
                res = GrFloodFill(dc, _x, _y, not_color, _z, dc->depth_buf);
                if (dc->flags & DCF_JUST_MIRROR)
                        goto gr_done;
        }
        res = GrFloodFill(dc, x1, y1, not_color, z1, dc->depth_buf);
gr_done:
        dc->flags = old_flags;

        return res;
}

#help_index "Graphics;Graphics/Device Contexts"

Option(OPTf_WARN_HEADER_MISMATCH, OFF);
public I64 GrBlot3(CDC *dc=gr.dc, I64 x1, I64 y1, I64 z1, CDC *img)
{//3D. Clipping and transformation.
        CColorROPU32     old_color = dc->color;
        I64                      color,
                                 reg i, j, w = img->width, h = img->height, 
                                         d1, dx1, dy1, dz1, 
                                 reg d2, dx2, dy2, dz2, 
                                         adx1, ady1, adz1, adx2, ady2, adz2, x2, y2, z2, x3, y3, z3, 
                                         dw, reg dh, x, y, _x1, _y1, _z1, _x2, _y2, _z2, _x3, _y3, _z3, last_x, last_y, res = 0;
        Bool                     first;
        CDC                             *old_brush = dc->brush;

        if (dc->depth_buf || dc->flags & (DCF_TRANSFORMATION | DCF_SYMMETRY))
        {
                x2 = x1 + w;
                y2 = y1;
                z2 = z1;
                x3 = x1;
                y3 = y1 + h;
                z3 = z1;
                if (dc->flags & DCF_TRANSFORMATION)
                {
                        (*dc->transform)(dc, &x1, &y1, &z1);
                        (*dc->transform)(dc, &x2, &y2, &z2);
                        (*dc->transform)(dc, &x3, &y3, &z3);
                }
                if (dc->flags & DCF_SYMMETRY)
                {
                        _x1 = x1;
                        _y1 = y1;
                        _z1 = z1;
                        DCReflect(dc, &_x1, &_y1, &_z1);
                        _x2 = x2;
                        _y2 = y2;
                        _z2 = z2;
                        DCReflect(dc, &_x2, &_y2, &_z2);
                        _x3 = x3;
                        _y3 = y3;
                        _z3 = z3;
                        DCReflect(dc, &_x3, &_y3, &_z3);
                        dx1 = _x2 - _x1;
                        dy1 = _y2 - _y1;
                        dz1 = _z2 - _z1;
                        dx2 = _x3 - _x1;
                        dy2 = _y3 - _y1;
                        dz2 = _z3 - _z1;
                        adx1 = AbsI64(dx1);
                        ady1 = AbsI64(dy1);
                        adz1 = AbsI64(dz1);
                        adx2 = AbsI64(dx2);
                        ady2 = AbsI64(dy2);
                        adz2 = AbsI64(dz2);

                        if (adx1 >= ady1)
                        {
                                if (adx1 >= adz1)
                                        d1 = adx1;
                                else
                                        d1 = adz1;
                        }
                        else
                        {
                                if (ady1 >= adz1)
                                        d1 = ady1;
                                else
                                        d1 = adz1;
                        }
                        if (adx2 >= ady2)
                        {
                                if (adx2 >= adz2)
                                        d2 = adx2;
                                else
                                        d2 = adz2;
                        }
                        else
                        {
                                if (ady2 >= adz2)
                                        d2 = ady2;
                                else
                                        d2 = adz2;
                        }

                        if (AbsI64(d1) != w || AbsI64(d2) != h)
                        {
                                d1 <<= 1;
                                d2 <<= 1;
                        }
                        if (d1)
                        {
                                dx1 = dx1 << 32 / d1;
                                dy1 = dy1 << 32 / d1;
                                dz1 = dz1 << 32 / d1;
                        }
                        else
                                goto normal_image;
                        if (d2)
                        {
                                dx2 = dx2 << 32 / d2;
                                dy2 = dy2 << 32 / d2;
                                dz2 = dz2 << 32 / d2;
                        }
                        else
                                goto normal_image;
                        dc->brush = NULL;
                        x = 0;
                        y = 0;
                        dw = w << 32 / d1;
                        dh = h << 32 / d2;

                        first = TRUE;
                        _x1 <<= 32;
                        _y1 <<= 32;
                        _z1 <<= 32;
                        for (j = 0; j <= d1; j++)
                        {
                                _x2 = _x1;
                                _y2 = _y1;
                                _z2 = _z1;
                                y = 0;
                                for (i = 0; i <= d2; i++)
                                {
                                        if (_x2.i32[1] != last_x || _y2.i32[1] != last_y || first)
                                        {
                                                if ((color = GrPeek(img, x.i32[1], y.i32[1])) >= 0)
                                                {
                                                        if (dc->color.c0.rop == ROPB_MONO)
                                                        {
                                                                if (color)
                                                                {
                                                                        dc->color = old_color & ~ROPF_DITHER;
                                                                        if (dc->depth_buf)
                                                                        {
                                                                                dc->db_z = _z2.i32[1];
                                                                                GrPlot1(dc, _x2.i32[1], _y2.i32[1]);
                                                                        }
                                                                        else
                                                                                GrPlot(dc, _x2.i32[1], _y2.i32[1]);
                                                                }
                                                        }
                                                        else
                                                        {
                                                                if (color != TRANSPARENT)
                                                                {
                                                                        dc->color = old_color & ~COLORROP_NO_ROP0_MASK|color;
                                                                        if (dc->depth_buf)
                                                                        {
                                                                                dc->db_z = _z2.i32[1];
                                                                                GrPlot1(dc, _x2.i32[1], _y2.i32[1]);
                                                                        }
                                                                        else
                                                                                GrPlot(dc, _x2.i32[1], _y2.i32[1]);
                                                                }
                                                        }
                                                }
                                        }
                                        first = FALSE;
                                        last_x = _x2.i32[1];
                                        last_y = _y2.i32[1];
                                        _x2 += dx2;
                                        _y2 += dy2;
                                        _z2 += dz2;
                                        y += dh;
                                }
                                _x1 += dx1;
                                _y1 += dy1;
                                _z1 += dz1;
                                x += dw;
                        }
                        res = 1;
normal_image:
                        if (dc->flags & DCF_JUST_MIRROR)
                                goto gr_done;
                }
                dx1 = x2 - x1;
                dy1 = y2 - y1;
                dz1 = z2 - z1;
                dx2 = x3 - x1;
                dy2 = y3 - y1;
                dz2 = z3 - z1;
                adx1 = AbsI64(dx1);
                ady1 = AbsI64(dy1);
                adz1 = AbsI64(dz1);
                adx2 = AbsI64(dx2);
                ady2 = AbsI64(dy2);
                adz2 = AbsI64(dz2);

                if (adx1 >= ady1)
                {
                        if (adx1 >= adz1)
                                d1 = adx1;
                        else
                                d1 = adz1;
                }
                else
                {
                        if (ady1 >= adz1)
                                d1 = ady1;
                        else
                                d1 = adz1;
                }
                if (adx2 >= ady2)
                {
                        if (adx2 >= adz2)
                                d2 = adx2;
                        else
                                d2 = adz2;
                }
                else
                {
                        if (ady2 >= adz2)
                                d2 = ady2;
                        else
                                d2 = adz2;
                }
                if (AbsI64(d1) != w || AbsI64(d2) != h)
                {
                        d1 <<= 1;
                        d2 <<= 1;
                }
                if (d1)
                {
                        dx1 = dx1 << 32 / d1;
                        dy1 = dy1 << 32 / d1;
                        dz1 = dz1 << 32 / d1;
                }
                else
                        goto gr_done;
                if (d2)
                {
                        dx2 = dx2 << 32 / d2;
                        dy2 = dy2 << 32 / d2;
                        dz2 = dz2 << 32 / d2;
                }
                else
                        goto gr_done;
                dc->brush = NULL;
                x = 0;
                y = 0;
                dw = w << 32 / d1;
                dh = h << 32 / d2;

                first = TRUE;
                x1 <<= 32;
                y1 <<= 32;
                z1 <<= 32;
                for (j = 0; j <= d1; j++)
                {
                        x2 = x1;
                        y2 = y1;
                        z2 = z1;
                        y = 0;
                        for (i = 0; i <= d2; i++)
                        {
                                if (x2.i32[1] != last_x || y2.i32[1] != last_y || first)
                                {
                                        if ((color = GrPeek(img, x.i32[1], y.i32[1])) >= 0)
                                        {
                                                if (dc->color.c0.rop == ROPB_MONO)
                                                {
                                                        if (color)
                                                        {
                                                                dc->color = old_color & ~ROPF_DITHER;
                                                                if (dc->depth_buf)
                                                                {
                                                                        dc->db_z = z2.i32[1];
                                                                        GrPlot1(dc, x2.i32[1], y2.i32[1]);
                                                                }
                                                                else
                                                                        GrPlot(dc, x2.i32[1], y2.i32[1]);
                                                        }
                                                }
                                                else
                                                {
                                                        if (color != TRANSPARENT)
                                                        {
                                                                dc->color = old_color & ~COLORROP_NO_ROP0_MASK | color;//COLOR
                                                                if (dc->depth_buf)
                                                                {
                                                                        dc->db_z = z2.i32[1];
                                                                        GrPlot1(dc, x2.i32[1], y2.i32[1]);
                                                                }
                                                                else
                                                                        GrPlot(dc, x2.i32[1], y2.i32[1]);
                                                        }
                                                }
                                        }
                                }
                                first = FALSE;
                                last_x = x2.i32[1];
                                last_y = y2.i32[1];
                                x2 += dx2;
                                y2 += dy2;
                                z2 += dz2;
                                y += dh;
                        }
                        x1 += dx1;
                        y1 += dy1;
                        z1 += dz1;
                        x += dw;
                }
                res = 1;        //TODO: check off screen
        }
        else
                res = GrBlot(dc, x1, y1, img);
gr_done:
        dc->color = old_color;
        dc->brush = old_brush;

        return res;
}
Option(OPTf_WARN_HEADER_MISMATCH, ON);

#help_index "Graphics"

public Bool Gr2Bezier3(CDC *dc=gr.dc, CD3I32 *ctrl)
{//2nd order. Clipping and transform and thick.
        Bool     res = FALSE;
        I64              i, x, y, z, old_flags = dc->flags;
        CD3I32  *ctrl2 = NULL, *ctrl3 = NULL;

        if (dc->flags & DCF_TRANSFORMATION)
        {
                ctrl2 = MAlloc(sizeof(CD3I32) * 3);
                for (i = 0; i < 3; i++)
                {
                        x = ctrl[i].x;
                        y = ctrl[i].y;
                        z = ctrl[i].z;
                        (*dc->transform)(dc, &x, &y, &z);
                        ctrl2[i].x = x;
                        ctrl2[i].y = y;
                        ctrl2[i].z = z;
                }
                dc->flags &= ~DCF_TRANSFORMATION;
                ctrl = ctrl2;
        }
        if (dc->flags & DCF_SYMMETRY)
        {
                ctrl3 = MAlloc(sizeof(CD3I32) * 3);
                for (i = 0; i < 3; i++)
                {
                        x = ctrl[i].x;
                        y = ctrl[i].y;
                        z = ctrl[i].z;
                        DCReflect(dc, &x, &y, &z);
                        ctrl3[i].x = x;
                        ctrl3[i].y = y;
                        ctrl3[i].z = z;
                }
                dc->flags &= ~DCF_SYMMETRY;
                res = Bezier2(dc, ctrl3, &GrPlot3);
                if (dc->flags & DCF_JUST_MIRROR)
                        goto gr_done;
        }

        res |= Bezier2(dc, ctrl, &GrPlot3);
gr_done:
        Free(ctrl2);
        Free(ctrl3);
        dc->flags = old_flags;

        return res;
}

public Bool Gr3Bezier3(CDC *dc=gr.dc, CD3I32 *ctrl)
{//3rd order. Clipping and transform and thick.
        Bool     res = FALSE;
        I64              i, x, y, z, old_flags = dc->flags;
        CD3I32  *ctrl2 = NULL, *ctrl3 = NULL;

        if (dc->flags & DCF_TRANSFORMATION)
        {
                ctrl2 = MAlloc(sizeof(CD3I32) * 4);
                for (i = 0; i < 4; i++)
                {
                        x = ctrl[i].x;
                        y = ctrl[i].y;
                        z = ctrl[i].z;
                        (*dc->transform)(dc, &x, &y, &z);
                        ctrl2[i].x = x;
                        ctrl2[i].y = y;
                        ctrl2[i].z = z;
                }
                dc->flags &= ~DCF_TRANSFORMATION;
                ctrl = ctrl2;
        }
        if (dc->flags & DCF_SYMMETRY)
        {
                ctrl3 = MAlloc(sizeof(CD3I32) * 4);
                for (i = 0; i < 4; i++)
                {
                        x = ctrl[i].x;
                        y = ctrl[i].y;
                        z = ctrl[i].z;
                        DCReflect(dc, &x, &y, &z);
                        ctrl3[i].x = x;
                        ctrl3[i].y = y;
                        ctrl3[i].z = z;
                }
                dc->flags &= ~DCF_SYMMETRY;
                res = Bezier3(dc, ctrl3, &GrPlot3);
                if (dc->flags & DCF_JUST_MIRROR)
                        goto gr_done;
        }

        res |= Bezier3(dc, ctrl, &GrPlot3);
gr_done:
        Free(ctrl2);
        Free(ctrl3);
        dc->flags = old_flags;

        return res;
}

public I64 Gr2BSpline3(CDC *dc=gr.dc, CD3I32 *ctrl, I64 count, Bool closed=FALSE)
{//2nd order. Clipping and transform and thick.
        Bool     res = FALSE;
        I64              i, x, y, z, old_flags = dc->flags;
        CD3I32  *ctrl2 = NULL, *ctrl3 = NULL;

        if (dc->flags & DCF_TRANSFORMATION)
        {
                ctrl2 = MAlloc(sizeof(CD3I32) * count);
                for (i = 0; i < count; i++)
                {
                        x = ctrl[i].x;
                        y = ctrl[i].y;
                        z = ctrl[i].z;
                        (*dc->transform)(dc, &x, &y, &z);
                        ctrl2[i].x = x;
                        ctrl2[i].y = y;
                        ctrl2[i].z = z;
                }
                dc->flags &= ~DCF_TRANSFORMATION;
                ctrl = ctrl2;
        }
        if (dc->flags & DCF_SYMMETRY)
        {
                ctrl3 = MAlloc(sizeof(CD3I32) * count);
                for (i = 0;i < count; i++)
                {
                        x = ctrl[i].x;
                        y = ctrl[i].y;
                        z = ctrl[i].z;
                        DCReflect(dc, &x, &y, &z);
                        ctrl3[i].x = x;
                        ctrl3[i].y = y;
                        ctrl3[i].z = z;
                }
                dc->flags &= ~DCF_SYMMETRY;
                res = BSpline2(dc, ctrl3, count, &GrPlot3, closed);
                if (dc->flags & DCF_JUST_MIRROR)
                        goto gr_done;
        }

        res |= BSpline2(dc, ctrl, count, &GrPlot3, closed);
gr_done:
        Free(ctrl2);
        Free(ctrl3);
        dc->flags = old_flags;

        return res;
}

public Bool Gr3BSpline3(CDC *dc=gr.dc, CD3I32 *ctrl, I64 count, Bool closed=FALSE)
{//3rd order. Clipping and transform and thick.
        Bool     res = FALSE;
        I64              i, x, y, z, old_flags = dc->flags;
        CD3I32  *ctrl2 = NULL, *ctrl3 = NULL;

        if (dc->flags & DCF_TRANSFORMATION)
        {
                ctrl2 = MAlloc(sizeof(CD3I32) * count);
                for (i = 0; i < count; i++)
                {
                        x = ctrl[i].x;
                        y = ctrl[i].y;
                        z = ctrl[i].z;
                        (*dc->transform)(dc, &x, &y, &z);
                        ctrl2[i].x = x;
                        ctrl2[i].y = y;
                        ctrl2[i].z = z;
                }
                dc->flags &= ~DCF_TRANSFORMATION;
                ctrl = ctrl2;
        }
        if (dc->flags & DCF_SYMMETRY)
        {
                ctrl3 = MAlloc(sizeof(CD3I32) * count);
                for (i = 0; i < count; i++)
                {
                        x = ctrl[i].x;
                        y = ctrl[i].y;
                        z = ctrl[i].z;
                        DCReflect(dc, &x, &y, &z);
                        ctrl3[i].x = x;
                        ctrl3[i].y = y;
                        ctrl3[i].z = z;
                }
                dc->flags &= ~DCF_SYMMETRY;
                res = BSpline3(dc, ctrl3, count, &GrPlot3, closed);
                if (dc->flags & DCF_JUST_MIRROR)
                        goto gr_done;
        }

        res |= BSpline3(dc, ctrl, count, &GrPlot3, closed);
gr_done:
        Free(ctrl2);
        Free(ctrl3);
        dc->flags = old_flags;

        return res;
}

public I64 GrFillTri0(CDC *dc=gr.dc, CD3I32 *p1, CD3I32 *p2, CD3I32 *p4)
{//3D. Returns count of pixs changed
        I64              x1, x2, y1, y2, z1, z2, dx1, dy1, dz1, dx2, dy2, dz2, res = 0, i, min, max;
        CTask   *win_task;

        if (AbsI64(p1->y - p2->y) + AbsI64(p1->y - p4->y) <= AbsI64(p1->x - p2->x) + AbsI64(p1->x - p4->x))
        {//p1 is min x
                if (p4->x < p2->x)
                        SwapI64(&p4, &p2);
                if (p2->x < p1->x)
                        SwapI64(&p2, &p1);

                        //p2y <= p4y
                if (p4->y < p2->y)
                        SwapI64(&p4, &p2);

                min = 0;
                max = dc->height;
                if (dc->flags & DCF_SCREEN_BITMAP)
                {
                        win_task = dc->win_task;
                        min -= win_task->scroll_y + win_task->pix_top;
                        max -= win_task->scroll_y + win_task->pix_top;
                        if (max > win_task->pix_bottom - (win_task->scroll_y + win_task->pix_top))
                                max = win_task->pix_bottom - (win_task->scroll_y + win_task->pix_top);
                }

                if ((dy2 = p4->y - p1->y) < 0)
                {
                        dy1 = p2->y - p1->y;
                        dx1 = (p1->x - p2->x) << 32 / dy1;
                        dz1 = (p1->z - p2->z) << 32 / dy1;

                        dx2 = (p1->x - p4->x) << 32 / dy2;
                        dz2 = (p1->z - p4->z) << 32 / dy2;
                        x1 = x2 = p1->x << 32;
                        y1 = p1->y;
                        z1 = z2 = p1->z << 32;
                        if (y1 + dy2 < min)
                        {
                                i = min - (y1 + dy2);
                                if (i > -dy2)
                                        goto ft_done;
                                dy2 += i;
                        }
                        if (y1 >= max)
                        {
                                i = y1 - max + 1;
                                if (i > -dy2)
                                        i = -dy2;
                                dy2 += i;
                                y1 -= i;
                                x1 += dx1 * i;
                                x2 += dx2 * i;
                                z1 += dz1 * i;
                                z2 += dz2 * i;
                        }
                        while (dy2++)
                        {
                                res += GrHLine(dc, x1.i32[1], x2.i32[1], y1, z1.i32[1], z2.i32[1]);
                                y1--;
                                x1 += dx1;
                                x2 += dx2;
                                z1 += dz1;
                                z2 += dz2;
                        }
                        if (dy2 = p2->y-p4->y)
                        {
                                dx2 = (p4->x - p2->x) << 32 / dy2;
                                dz2 = (p4->z - p2->z) << 32 / dy2;
                                if (y1+dy2<min)
                                {
                                        i = min - (y1 + dy2);
                                        if (i > -dy2)
                                                goto ft_done;
                                        dy2 += i;
                                }
                                if (y1 >= max)
                                {
                                        i = y1 - max + 1;
                                        if (i > -dy2)
                                                goto ft_done;
                                        dy2 += i;
                                        y1 -= i;
                                        x1 += dx1 * i;
                                        x2 += dx2 * i;
                                        z1 += dz1 * i;
                                        z2 += dz2 * i;
                                }
                        }
                        while (dy2++ <= 0)
                        {
                                res += GrHLine(dc, x1.i32[1], x2.i32[1], y1, z1.i32[1], z2.i32[1]);
                                y1--;
                                x1 += dx1;
                                x2 += dx2;
                                z1 += dz1;
                                z2 += dz2;
                        }
                }
                else if ((dy2 = p2->y - p1->y) > 0)
                {
                        dy1 = p4->y  - p1->y;
                        dx1 = (p4->x - p1->x) << 32 / dy1;
                        dz1 = (p4->z - p1->z) << 32 / dy1;

                        dx2 = (p2->x-p1->x) << 32 / dy2;
                        dz2 = (p2->z-p1->z) << 32 / dy2;
                        x1 = x2 = p1->x << 32;
                        y1 = p1->y;
                        z1 = z2 = p1->z << 32;
                        if (y1 + dy2 >= max)
                        {
                                i = y1 + dy2 - max + 1;
                                if (i > dy2)
                                        goto ft_done;
                                dy2 -= i;
                        }
                        if (y1 < min)
                        {
                                i = min - y1;
                                if (i > dy2)
                                        i = dy2;
                                dy2 -= i;
                                y1 += i;
                                x1 += dx1 * i;
                                x2 += dx2 * i;
                                z1 += dz1 * i;
                                z2 += dz2 * i;
                        }
                        while (dy2--)
                        {
                                res += GrHLine(dc, x1.i32[1], x2.i32[1], y1, z1.i32[1], z2.i32[1]);
                                y1++;
                                x1 += dx1;
                                x2 += dx2;
                                z1 += dz1;
                                z2 += dz2;
                        }
                        if (dy2 = p4->y - p2->y)
                        {
                                dx2 = (p4->x - p2->x) << 32 / dy2;
                                dz2 = (p4->z - p2->z) << 32 / dy2;
                                if (y1 + dy2 >= max)
                                {
                                        i = y1 + dy2 - max + 1;
                                        if (i > dy2)
                                                goto ft_done;
                                        dy2 -= i;
                                }
                                if (y1 < min)
                                {
                                        i = min - y1;
                                        if (i > dy2)
                                                goto ft_done;
                                        dy2 -= i;
                                        y1 += i;
                                        x1 += dx1 * i;
                                        x2 += dx2 * i;
                                        z1 += dz1 * i;
                                        z2 += dz2 * i;
                                }
                        }
                        while (dy2-- >= 0)
                        {
                                res += GrHLine(dc, x1.i32[1], x2.i32[1], y1, z1.i32[1], z2.i32[1]);
                                y1++;
                                x1 += dx1;
                                x2 += dx2;
                                z1 += dz1;
                                z2 += dz2;
                        }
                }
                else
                {
                        if (dy1 = p2->y - p1->y)
                        {
                                dx1 = (p2->x - p1->x) << 32 / dy1;
                                dz1 = (p2->z - p1->z) << 32 / dy1;
                                if (dy2 = p2->y - p4->y)
                                {
                                        dx2 = (p2->x - p4->x) << 32 / dy2;
                                        dz2 = (p2->z - p4->z) << 32 / dy2;
                                }
                                else
                                {
                                        dx2 = 0;
                                        dz2 = 0;
                                }
                                x1 = x2 = p2->x << 32;
                                y1 = p2->y;
                                z1 = z2 = p2->z << 32;
                                if (y1 < min)
                                {
                                        i = min - y1;
                                        if (i > -dy1)
                                                i = -dy1;
                                        dy1 += i;
                                        y1 += i;
                                        x1 += dx1 * i;
                                        x2 += dx2 * i;
                                        z1 += dz1 * i;
                                        z2 += dz2 * i;
                                }
                                while (dy1++ <= 0)
                                {
                                        if (y1 < max)
                                                res += GrHLine(dc, x1.i32[1], x2.i32[1], y1, z1.i32[1], z2.i32[1]);
                                        y1++;
                                        x1 += dx1;
                                        x2 += dx2;
                                        z1 += dz1;
                                        z2 += dz2;
                                }
                        }
                        if (dy1 = p4->y - p1->y)
                        {
                                dx1 = (p1->x - p4->x) << 32 / dy1;
                                dz1 = (p1->z - p4->z) << 32 / dy1;
                                if (dy2 = p4->y - p2->y)
                                {
                                        dx2 = (p2->x - p4->x) << 32 / dy2;
                                        dz2 = (p2->z - p4->z) << 32 / dy2;
                                }
                                else
                                {
                                        dx2 = 0;
                                        dz2 = 0;
                                }
                                x1 = x2 = p4->x << 32;
                                y1 = p4->y;
                                z1 = z2 = p4->z << 32;
                                if (y1-dy1 < min)
                                {
                                        i = min-(y1 - dy1);
                                        if (i > dy1)
                                                goto ft_done;
                                        dy1 -= i;
                                }
                                if (y1 >= max)
                                {
                                        i = y1 - max + 1;
                                        if (i > dy1)
                                                goto ft_done;
                                        dy1 -= i;
                                        y1 -= i;
                                        x1 += dx1 * i;
                                        x2 += dx2 * i;
                                        z1 += dz1 * i;
                                        z2 += dz2 * i;
                                }
                                while (dy1-- >= 0)
                                {
                                        res += GrHLine(dc, x1.i32[1], x2.i32[1], y1, z1.i32[1], z2.i32[1]);
                                        y1--;
                                        x1 += dx1;
                                        x2 += dx2;
                                        z1 += dz1;
                                        z2 += dz2;
                                }
                        }
                }
        }
        else
        {
//p1 is min y
                if (p4->y < p2->y)
                        SwapI64(&p4, &p2);
                if (p2->y < p1->y)
                        SwapI64(&p2, &p1);

                        //p2x <= p4x
                if (p4->x < p2->x)
                        SwapI64(&p4, &p2);

                min = 0;
                max = dc->width;
                if (dc->flags & DCF_SCREEN_BITMAP)
                {
                        win_task = dc->win_task;
                        min -= win_task->scroll_x + win_task->pix_left;
                        max -= win_task->scroll_x + win_task->pix_left;
                        if (max > win_task->pix_right - (win_task->scroll_x + win_task->pix_left))
                                max = win_task->pix_right - (win_task->scroll_x + win_task->pix_left);
                }

                if ((dx2 = p4->x-p1->x) < 0)
                {
                        dx1 = p2->x - p1->x;
                        dy1 = (p1->y - p2->y) << 32 / dx1;
                        dz1 = (p1->z - p2->z) << 32 / dx1;

                        dy2 = (p1->y - p4->y) << 32 / dx2;
                        dz2 = (p1->z - p4->z) << 32 / dx2;
                        y1 = y2 = p1->y << 32;
                        x1 = p1->x;
                        z1 = z2 = p1->z << 32;
                        if (x1 + dx2 < min)
                        {
                                i = min - (x1 + dx2);
                                if (i > -dx2)
                                        goto ft_done;
                                dx2 += i;
                        }
                        if (x1 >= max)
                        {
                                i = x1 - max + 1;
                                if (i > -dx2)
                                        i = -dx2;
                                dx2 += i;
                                x1 -= i;
                                y1 += dy1 * i;
                                y2 += dy2 * i;
                                z1 += dz1 * i;
                                z2 += dz2 * i;
                        }
                        while (dx2++)
                        {
                                res += GrVLine(dc, x1, y1.i32[1], y2.i32[1], z1.i32[1], z2.i32[1]);
                                x1--;
                                y1 += dy1;
                                y2 += dy2;
                                z1 += dz1;
                                z2 += dz2;
                        }
                        if (dx2 = p2->x - p4->x)
                        {
                                dy2 = (p4->y - p2->y) << 32 / dx2;
                                dz2 = (p4->z - p2->z) << 32 / dx2;
                                if (x1 + dx2 < min)
                                {
                                        i = min - (x1 + dx2);
                                        if (i > -dx2)
                                                goto ft_done;
                                        dx2 += i;
                                }
                                if (x1 >= max)
                                {
                                        i = x1 - max + 1;
                                        if (i > -dx2)
                                                goto ft_done;
                                        dx2 += i;
                                        x1 -= i;
                                        y1 += dy1 * i;
                                        y2 += dy2 * i;
                                        z1 += dz1 * i;
                                        z2 += dz2 * i;
                                }
                        }
                        while (dx2++ <= 0)
                        {
                                res += GrVLine(dc, x1, y1.i32[1], y2.i32[1], z1.i32[1], z2.i32[1]);
                                x1--;
                                y1 += dy1;
                                y2 += dy2;
                                z1 += dz1;
                                z2 += dz2;
                        }
                }
                else if ((dx2 = p2->x - p1->x) > 0)
                {
                        dx1 = p4->x - p1->x;
                        dy1 = (p4->y - p1->y) << 32 / dx1;
                        dz1 = (p4->z - p1->z) << 32 / dx1;

                        dy2 = (p2->y - p1->y) << 32 / dx2;
                        dz2 = (p2->z - p1->z) << 32 / dx2;
                        y1 = y2 = p1->y << 32;
                        x1 = p1->x;
                        z1 = z2 = p1->z << 32;
                        if (x1 + dx2 >= max)
                        {
                                i = x1 + dx2 - max + 1;
                                if (i > dx2)
                                        goto ft_done;
                                dx2 -= i;
                        }
                        if (x1 < min)
                        {
                                i = min - x1;
                                if (i > dx2)
                                        i = dx2;
                                dx2 -= i;
                                x1 += i;
                                y1 += dy1 * i;
                                y2 += dy2 * i;
                                z1 += dz1 * i;
                                z2 += dz2 * i;
                        }
                        while (dx2--)
                        {
                                res += GrVLine(dc, x1, y1.i32[1], y2.i32[1], z1.i32[1], z2.i32[1]);
                                x1++;
                                y1 += dy1;
                                y2 += dy2;
                                z1 += dz1;
                                z2 += dz2;
                        }
                        if (dx2 = p4->x - p2->x)
                        {
                                dy2 = (p4->y - p2->y) << 32 / dx2;
                                dz2 = (p4->z - p2->z) << 32 / dx2;
                                if (x1 + dx2 >= max)
                                {
                                        i = x1 + dx2 - max + 1;
                                        if (i > dx2)
                                                goto ft_done;
                                        dx2 -= i;
                                }
                                if (x1 < min)
                                {
                                        i = min - x1;
                                        if (i > dx2)
                                                goto ft_done;
                                        dx2 -= i;
                                        x1 += i;
                                        y1 += dy1 * i;
                                        y2 += dy2 * i;
                                        z1 += dz1 * i;
                                        z2 += dz2 * i;
                                }
                        }
                        while (dx2-- >= 0)
                        {
                                res += GrVLine(dc, x1, y1.i32[1], y2.i32[1], z1.i32[1], z2.i32[1]);
                                x1++;
                                y1 += dy1;
                                y2 += dy2;
                                z1 += dz1;
                                z2 += dz2;
                        }
                }
                else
                {
                        if (dx1 = p2->x - p1->x)
                        {
                                dy1 = (p2->y - p1->y) << 32 / dx1;
                                dz1 = (p2->z - p1->z) << 32 / dx1;
                                if (dx2 = p2->x - p4->x)
                                {
                                        dy2 = (p2->y - p4->y) << 32 / dx2;
                                        dz2 = (p2->z - p4->z) << 32 / dx2;
                                }
                                else
                                {
                                        dy2 = 0;
                                        dz2 = 0;
                                }
                                y1 = y2 = p2->y << 32;
                                x1 = p2->x;
                                z1 = z2 = p2->z << 32;
                                if (x1 < min)
                                {
                                        i = min - x1;
                                        if (i > -dx1)
                                                i = -dx1;
                                        dx1 += i;
                                        x1 += i;
                                        y1 += dy1 * i;
                                        y2 += dy2 * i;
                                        z1 += dz1 * i;
                                        z2 += dz2 * i;
                                }
                                while (dx1++ <= 0)
                                {
                                        if (x1 < max)
                                                res += GrVLine(dc, x1, y1.i32[1], y2.i32[1], z1.i32[1], z2.i32[1]);
                                        x1++;
                                        y1 += dy1;
                                        y2 += dy2;
                                        z1 += dz1;
                                        z2 += dz2;
                                }
                        }
                        if (dx1 = p4->x - p1->x)
                        {
                                dy1 = (p1->y - p4->y) << 32 / dx1;
                                dz1 = (p1->z - p4->z) << 32 / dx1;
                                if (dx2 = p4->x - p2->x)
                                {
                                        dy2 = (p2->y - p4->y) << 32 / dx2;
                                        dz2 = (p2->z - p4->z) << 32 / dx2;
                                }
                                else
                                {
                                        dy2 = 0;
                                        dz2 = 0;
                                }
                                y1 = y2 = p4->y << 32;
                                x1 = p4->x;
                                z1 = z2 = p4->z << 32;
                                if (x1 - dx1 < min)
                                {
                                        i = min - (x1 - dx1);
                                        if (i > dx1)
                                                goto ft_done;
                                        dx1 -= i;
                                }
                                if (x1 >= max)
                                {
                                        i = x1 - max + 1;
                                        if (i > dx1)
                                                goto ft_done;
                                        dx1 -= i;
                                        x1 -= i;
                                        y1 += dy1 * i;
                                        y2 += dy2 * i;
                                        z1 += dz1 * i;
                                        z2 += dz2 * i;
                                }
                                while (dx1-- >= 0)
                                {
                                        res += GrVLine(dc, x1, y1.i32[1], y2.i32[1], z1.i32[1], z2.i32[1]);
                                        x1--;
                                        y1 += dy1;
                                        y2 += dy2;
                                        z1 += dz1;
                                        z2 += dz2;
                                }
                        }
                }
        }
ft_done:

        return res;
}