Trivial Solutions
                    The LoseThos 64-bit PC Operating System
File:/LT/Adam/Gr/GrBitMap.APZ
This, and all LoseThos files, are public domain.  Do whatever you like.
God is watching; God is just; God is never out-done in generosity.  He talks!

LoseThos code compiles with the 64-bit LoseThos compiler/assembler I wrote.
Boot the LoseThos CD and you can compile it.  Not in Kansas anymore!

Click here for preliminary compiler information you need.

#help_index "Graphics"

public BoolI64 GrPlot0(GrDC *dc,I64 x,I64 y)
{  //No clipping or transformation or pen width
  U8 *dst;
  I32 *db;
  U64 d,bit,not_bit,dist;
  ColorROPU32 c,c2;
  ColorROPU32 color=dc->color,bkcolor=dc->bkcolor;
  GrDC *mask;

  if (dc->flags & DCF_LOCATE_NEAREST) {
    dist=DistI64(x,y,dc->cur_x,dc->cur_y);
    if (dist<=dc->nearest_dist) {
      dc->nearest_sprite_elem_num=dc->cur_sprite_elem_num;
      dc->nearest_dist=dist;
    }
  }
  if (dc->flags & DCF_RECORD_EXTENTS) {
    if (x<dc->min_x) dc->min_x=x;
    if (x>dc->max_x) dc->max_x=x;
    if (y<dc->min_y) dc->min_y=y;
    if (y>dc->max_y) dc->max_y=y;
  }
  if (dc->flags & DCF_DONT_DRAW)
    return TRUE;
  d=dc->width_internal*y+x;
  if (db=dc->depth_buf) {
    db+=d;
    if (0<=dc->db_z<=*db)
      *db=dc->db_z;
    else
      return TRUE;
  }
  if (color.c1.rop&(ROPBF_DITHER|ROPBF_PROBABILITY_DITHER)) {
    if (color.c1.rop&ROPBF_PROBABILITY_DITHER) {
      if (Rand2U16<dc->dither_probability) {
        color.c1.rop=color.c0.rop;
        color.c0=color.c1;
      }
    } else {
      if ((x^y)&1) {
        color.c1.rop=color.c0.rop;
        color.c0=color.c1;
      }
    }
  }
  nobound_switch (dc->type) {
    case DCT_COLOR4:
      dst=dc->body+d>>3;
      d=dc->plane_size;
      bit=1<<(x&7);
      not_bit=~bit;
      c=color.c0.color;
      nobound_switch (color.c0.rop) {
        case ROPB_EQU:
        case ROPB_CLEAR_MASK_EQU:
        case ROPB_CLEAR_MASK_TRANSPARENT:
        case ROPB_TRANSPARENT:
          if (c & 1)
            *dst|=bit;
          else
            *dst&=not_bit;
          dst+=d;
          if (c & 2)
            *dst|=bit;
          else
            *dst&=not_bit;
          dst+=d;
          if (c & 4)
            *dst|=bit;
          else
            *dst&=not_bit;
          dst+=d;
          if (c & 8)
            *dst|=bit;
          else
            *dst&=not_bit;
          break;
        case ROPB_COLLISION:
          c2=0;
          if (*dst&bit)
            c2+=1;
          dst+=d;
          if (*dst&bit)
            c2+=2;
          dst+=d;
          if (*dst&bit)
            c2+=4;
          dst+=d;
          if (*dst&bit)
            c2+=8;
          if (c2.c0.color!=bkcolor.c0.color)
            dc->collision_cnt++;
          break;
        case ROPB_XOR:
          if (c & 1) *dst^=bit;
          dst+=d;
          if (c & 2) *dst^=bit;
          dst+=d;
          if (c & 4) *dst^=bit;
          dst+=d;
          if (c & 8) *dst^=bit;
          break;
      }
      break;
    case DCT_MONO:
      c=color&COLORROP_COLORS_MASK;
      nobound_switch (color.c0.rop) {
        case ROPB_EQU:
        case ROPB_CLEAR_MASK_EQU:
        case ROPB_CLEAR_MASK_TRANSPARENT:
        case ROPB_TRANSPARENT:
          BEqu(dc->body,d,c);
          break;
        case ROPB_COLLISION:
          if (c) {
            if (bkcolor&COLORROP_COLORS_MASK)
              dc->collision_cnt+=Bt(dc->body,d);
            else
              dc->collision_cnt+=!Bt(dc->body,d);
          } else {
            if (bkcolor&COLORROP_COLORS_MASK)
              dc->collision_cnt+=!Bt(dc->body,d);
            else
              dc->collision_cnt+=Bt(dc->body,d);
          }
          break;
        case ROPB_XOR:
          if (c)
            Btc(dc->body,d);
          break;
      }
      break;
  }
  if (mask=dc->mask) {
    mask->brush=dc->brush;
    mask->color=WHITE;
    if (dc->color.c0.rop==ROPB_CLEAR_MASK_EQU)
      mask->color=ROPB_EQU+BLACK;
    else if (dc->color.c0.rop==ROPB_CLEAR_MASK_TRANSPARENT)
      mask->color=ROPB_TRANSPARENT+BLACK;
    else
      mask->color.c0.rop=dc->color.c0.rop;
    mask->win_task=dc->win_task;
    if (mask->alias)
      mask->alias->flags|=DCF_DIRTY;
    GrPlot0(mask,x,y);
  }
  return TRUE;
}

public I64 GrPeek0(GrDC *dc,I64 x,I64 y)
{  //No clipping or transformation
  U64 d,c,bit;
  U8 *src;

  d=dc->width_internal*y+x;
  nobound_switch (dc->type) {
    case DCT_COLOR4:
      if (dc->mask && !Bt(dc->mask->body,d))
        return MAX_I32;
      src=dc->body+d>>3;
      bit=1<<(x&7);
      d=dc->plane_size;
      c=0;
      if (*src & bit)
        c|=1;
      src+=d;
      if (*src & bit)
        c|=2;
      src+=d;
      if (*src & bit)
        c|=4;
      src+=d;
      if (*src & bit)
        c|=8;

      return c;
    case DCT_MONO:
      if (dc->mask && !Bt(dc->mask->body,d))
        return MAX_I32;
      return Bt(dc->body,d);
  }
}

#help_index "Graphics;Graphics/DCs"

public BoolI64 GrDCBlot(GrDC *dc,I64 _x,I64 _y,GrDC *img)
{  //Clipping but not transformation
  I64 x=_x,y=_y,i,j,k,k1,kk,kk1,m,w1,h1,w2,h2,plane,plane1,
      leading_bits,leading_bit_mask,whole_bytes,trailing_bits,trailing_bit_mask,
      reg bit_shift,p,p1,dist,win_z_buf_line_inc,win_z_buf_line_dec,win_z_num,
      color_byte,plane_size,plane_size1,plane_limit,plane_limit1;
  U8 reg *dst,*src;
  U16 reg *win_z_buf_ptr;
  ColorROPU32 color,color2,c;
  I32 *db=dc->depth_buf;
  GrDC *mask;
  TaskStruct *win_task;

  if (dc->flags & DCF_SCREEN_BITMAP) {
    win_task=dc->win_task;
    x+=win_task->win_scroll_x;
    y+=win_task->win_scroll_y;
  }
  w1=x<0 ?-x:0;
  h1=y<0 ?-y:0;
  w2=img->width;
  h2=img->height;
  if (dc->flags & DCF_SCREEN_BITMAP) {
    x+=win_task->win_pixel_left;
    y+=win_task->win_pixel_top;
  }
  if (dc->flags & DCF_LOCATE_NEAREST) { //TODO:Untested
    dist=DistI64(x+img->width>>1,y+img->height>>1,dc->cur_x,dc->cur_y);
    if (dist<=dc->nearest_dist) {
      dc->nearest_sprite_elem_num=dc->cur_sprite_elem_num;
      dc->nearest_dist=dist;
    }
  }
  if (dc->flags & DCF_SCREEN_BITMAP) {
    if (x+w1<0) w1=-x;
    if (x+w2>win_task->win_pixel_right+1)
      w2=win_task->win_pixel_right+1-x;

    if (y+h1<0) h1=-y;
    if (y+h2>win_task->win_pixel_bottom+1)
      h2=win_task->win_pixel_bottom+1-y;
  }
  if (x+w2>dc->width)
    w2=dc->width-x;
  if (y+h2>dc->height)
    h2=dc->height-y;
  if (w1<w2<=img->width && h1<h2<=img->height) {
    if (dc->flags & DCF_RECORD_EXTENTS) {
      if (x+w1<dc->min_x) dc->min_x=x+w1;
      if (x+w2-1>dc->max_x) dc->max_x=x+w2-1;
      if (y+h1<dc->min_y) dc->min_y=y+h1;
      if (y+h2-1>dc->max_y) dc->max_y=y+h2-1;
    }
    if (dc->flags & DCF_DONT_DRAW)
      return TRUE;
    dc->depth_buf=NULL;
    color=dc->color;
    leading_bits=-(w1+x)&7;
    leading_bit_mask=0xFF>>leading_bits;
    bit_shift=-x&7;
    whole_bytes=(w2-w1-leading_bits)>>3;
    if (whole_bytes<0) whole_bytes=0;
    trailing_bits=(x+w2)&7;
    trailing_bit_mask=0xFF<<trailing_bits;
    if (leading_bits+trailing_bits>w2-w1) {
      leading_bit_mask|=trailing_bit_mask;
      trailing_bits=0;
    }
    switch (color.c0.rop) {
      case ROPB_COLLISION: //TODO: Might want to check win_z_buf
        plane_size =img->plane_size<<3;
        plane_size1=dc->plane_size<<3;
        nobound_switch (dc->type) {
          case DCT_COLOR4:
            plane_limit=plane_size*4;
            plane_limit1=plane_size1*4;
            break;
          case DCT_MONO:
            plane_limit=plane_size;
            plane_limit1=plane_size1;
            break;
        }
        color =dc->bkcolor.c0.color;
        color2= img->bkcolor.c0.color;
        nobound_switch (img->type) {
          case DCT_COLOR4:
            k=h1*img->width_internal;
            k1=(h1+y)*dc->width_internal+x;
            for (j=h2-h1;j;j--) {
              for (i=w1;i<w2;i++) {
                c=0;
                for (plane1=0,p=1;plane1<plane_limit1;
                plane1+=plane_size1,p<<=1)
                  if (Bt(dc->body,k1+i+plane1))
                    c+=p;
                if (c!=color) {
                  c=0;
                  for (plane=0,p=1;plane<plane_limit;
                  plane+=plane_size,p<<=1)
                    if (Bt(img->body,k+i+plane))
                      c+=p;
                  if (c!=color2)
                    dc->collision_cnt++;
                }
              }
              k+=img->width_internal;
              k1+=dc->width_internal;
            }
            break;
          case DCT_MONO:
            k=h1*img->width_internal;
            k1=(h1+y)*dc->width_internal+x;
            for (j=h2-h1;j;j--) {
              for (i=w1;i<w2;i++) {
                c=0;
                for (plane1=0,p=1;plane1<plane_limit1;
                plane1+=plane_size1,p<<=1)
                  if (Bt(dc->body,k1+i+plane1))
                    c+=p;
                if (c!=color) {
                  if (Bt(img->body,k+i)!=color2)
                    dc->collision_cnt++;
                }
              }
              k+=img->width_internal;
              k1+=dc->width_internal;
            }
            break;
        }
        break;
      case ROPB_TRANSPARENT:
      case ROPB_CLEAR_MASK_TRANSPARENT:
        if (img->type==DCT_COLOR4) {
          plane_size=img->plane_size<<3;
          plane_limit=plane_size*4;
          color2=img->bkcolor.c0.color;
          k=h1*img->width_internal;
          if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags&DCF_ON_TOP) {
            for (j=h1;j<h2;j++) {
              for (i=w1;i<w2;i++) {
                c=0;
                for (plane=0,p=1;plane<plane_limit;
                plane+=plane_size,p<<=1)
                  if (Bt(img->body,k+i+plane))
                    c+=p;
                if (c!=color2) {
                  dc->color=c;
                  GrPlot0(dc,x+i,y+j);
                }
              }
              k+=img->width_internal;
            }
            } else {
            win_z_num         =win_task->win_z_num;
            win_z_buf_ptr     =gr_win_z_buf><(U8 *)+((h1+y)/FONT_HEIGHT*TEXT_COLS+(w1+x)/FONT_WIDTH)*sizeof(U16);
            win_z_buf_line_dec=((leading_bits?1:0)+whole_bytes+(trailing_bits?1:0))*sizeof(U16);
            win_z_buf_line_inc=TEXT_COLS*sizeof(U16)-win_z_buf_line_dec;
            for (j=h1;j<h2;j++) {
              if (win_z_num>=*win_z_buf_ptr++)
                color_byte=TRUE;
              else
                color_byte=FALSE;
              for (i=w1;i<w2;) {
                if (color_byte) {
                  c=0;
                  for (plane=0,p=1;plane<plane_limit;
                  plane+=plane_size,p<<=1)
                    if (Bt(img->body,k+i+plane))
                      c+=p;
                  if (c!=color2) {
                    dc->color=c;
                    GrPlot0(dc,x+i,y+j);
                  }
                }
                if (!((++i+x) &7) && i<w2) {
                  if (win_z_num>=*win_z_buf_ptr++)
                    color_byte=TRUE;
                  else
                    color_byte=FALSE;
                }
              }
              if ((j+y)&7==7)
                win_z_buf_ptr><(U8 *)+=win_z_buf_line_inc;
              else
                win_z_buf_ptr><(U8 *)-=win_z_buf_line_dec;
              k+=img->width_internal;
            }
            }
          dc->color=color;
        } else
          goto here1;
        break;
      default:
        here1:
      plane_size=img->plane_size<<3;
        plane_size1=dc->plane_size<<3;
        nobound_switch (dc->type) {
          case DCT_COLOR4:
            plane_limit=plane_size*4;
            plane_limit1=plane_size1*4;
            break;
          case DCT_MONO:
            plane_limit=plane_size;
            plane_limit1=plane_size1;
            break;
        }
        if (dc->type==DCT_MONO && img->type==DCT_COLOR4) {
          plane_limit=plane_size*4;
          k  = h1   *img->width_internal;
          kk1=(h1+y)*dc->width_internal+x;
          if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags&DCF_ON_TOP) {
            for (j=h2-h1;j;j--) {
              k1=kk1+w1;
              for (i=w1;i<w2;i++) {
                c=0;
                for (plane=0,p=1;plane<plane_limit;
                plane+=plane_size,p<<=1)
                  if (Bt(img->body,k+i+plane))
                    c+=p;
                if (c!=img->bkcolor)
                  Bts(dc->body,k1);
                k1++;
              }
              k+=img->width_internal;
              kk1+=dc->width_internal;
            }
            } else {
            win_z_num=win_task->win_z_num;
            win_z_buf_ptr=gr_win_z_buf><(U8 *)+((h1+y)/FONT_HEIGHT*TEXT_COLS+(w1+x)/FONT_WIDTH)*sizeof(U16);
            win_z_buf_line_dec=((leading_bits?1:0)+whole_bytes+(trailing_bits?1:0))*sizeof(U16);
            win_z_buf_line_inc=TEXT_COLS*sizeof(U16)-win_z_buf_line_dec;
            for (j=h1;j<h2;j++) {
              k1=kk1+w1;
              if (win_z_num>=*win_z_buf_ptr++)
                color_byte=TRUE;
              else
                color_byte=FALSE;
              for (i=w1;i<w2;) {
                if (color_byte) {
                  c=0;
                  for (plane=0,p=1;plane<plane_limit;
                  plane+=plane_size,p<<=1)
                    if (Bt(img->body,k+i+plane))
                      c+=p;
                  if (c!=img->bkcolor)
                    Bts(dc->body,k1);
                }
                if (!((++i+x) &7) && i<w2) {
                  if (win_z_num>=*win_z_buf_ptr++)
                    color_byte=TRUE;
                  else
                    color_byte=FALSE;
                }
                k1++;
              }
              if ((j+y)&7==7)
                win_z_buf_ptr><(U8 *)+=win_z_buf_line_inc;
              else
                win_z_buf_ptr><(U8 *)-=win_z_buf_line_dec;
              k+=img->width_internal;
              kk1+=dc->width_internal;
            }
            }
        } else {
          for (p1=0,p=1,plane=0,plane1=0;plane<plane_limit;
          plane+=plane_size,plane1+=plane_size1,p<<=1,p1++) {
            if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags&DCF_ON_TOP)
              win_z_buf_ptr=NULL;
            else {
              win_z_num=win_task->win_z_num;
              win_z_buf_ptr=gr_win_z_buf><(U8 *)+((h1+y)/FONT_HEIGHT*TEXT_COLS+(w1+x)/FONT_WIDTH)*sizeof(U16);
              win_z_buf_line_dec=((leading_bits?1:0)+whole_bytes+(trailing_bits?1:0))*sizeof(U16);
              win_z_buf_line_inc=TEXT_COLS*sizeof(U16)-win_z_buf_line_dec;
            }
            if (Bt(&color,p1))
              color_byte=255;
            else
              color_byte=0;
            kk = h1   *img ->width_internal  +w1;
            kk1=(h1+y)*dc->width_internal+x+w1;
            kk =(kk-bit_shift)&~7+bit_shift;
            nobound_switch (img->type) {
              case DCT_COLOR4:
                nobound_switch (color.c0.rop) {
                  case ROPB_EQU:
                  case ROPB_CLEAR_MASK_EQU:
                    if (win_z_buf_ptr)
                      for (j=h1;j<h2;j++) {
                        src=img->body +(kk+plane)>>3;
                        dst=dc->body+(kk1+plane1)>>3;
                        if (leading_bits) {
                          if (win_z_num>=*win_z_buf_ptr++)
                            *dst++=*dst&leading_bit_mask|*src><(U16 *)>>bit_shift&~leading_bit_mask;
                          else
                            dst++;
                          src++;
                        }
                        for (i=0;i<whole_bytes;i++,src++,dst++)
                          if (win_z_num>=*win_z_buf_ptr++)
                            *dst=*src><(U16 *)>>bit_shift;
                        if (trailing_bits && win_z_num>=*win_z_buf_ptr++)
                          *dst=*dst&trailing_bit_mask|*src><(U16 *)>>bit_shift&~trailing_bit_mask;
                        kk +=img->width_internal;
                        kk1+=dc->width_internal;
                        if ((j+y)&7==7)
                          win_z_buf_ptr><(U8 *)+=win_z_buf_line_inc;
                        else
                          win_z_buf_ptr><(U8 *)-=win_z_buf_line_dec;
                      }
                      else
                      for (j=h2-h1;j;j--) {
                        src=img->body +(kk+plane)>>3;
                        dst=dc->body+(kk1+plane1)>>3;
                        if (leading_bits) {
                          *dst++=*dst&leading_bit_mask|*src><(U16 *)>>bit_shift&~leading_bit_mask;
                          src++;
                        }
                        for (i=0;i<whole_bytes;i++,src++,dst++)
                          *dst=*src><(U16 *)>>bit_shift;
                        if (trailing_bits)
                          *dst=*dst&trailing_bit_mask|*src><(U16 *)>>bit_shift&~trailing_bit_mask;
                        kk +=img->width_internal;
                        kk1+=dc->width_internal;
                      }
                      break;
                  case ROPB_XOR:
                    if (win_z_buf_ptr)
                      for (j=h1;j<h2;j++) {
                        src=img->body +(kk+plane)>>3;
                        dst=dc->body+(kk1+plane1)>>3;
                        if (leading_bits) {
                          if (win_z_num>=*win_z_buf_ptr++)
                            *dst++=*dst&leading_bit_mask|(*dst^*src><(U16 *)>>bit_shift)&~leading_bit_mask;
                          else
                            dst++;
                          src++;
                        }
                        for (i=0;i<whole_bytes;i++,src++,dst++)
                          if (win_z_num>=*win_z_buf_ptr++)
                            *dst^=*src><(U16 *)>>bit_shift;
                        if (trailing_bits && win_z_num>=*win_z_buf_ptr++)
                          *dst=*dst&trailing_bit_mask|(*dst^*src><(U16 *)>>bit_shift)&~trailing_bit_mask;
                        kk +=img->width_internal;
                        kk1+=dc->width_internal;
                        if ((j+y)&7==7)
                          win_z_buf_ptr><(U8 *)+=win_z_buf_line_inc;
                        else
                          win_z_buf_ptr><(U8 *)-=win_z_buf_line_dec;
                      }
                      else
                      for (j=h2-h1;j;j--) {
                        src=img->body +(kk+plane)>>3;
                        dst=dc->body+(kk1+plane1)>>3;
                        if (leading_bits) {
                          *dst++=*dst&leading_bit_mask|(*dst^*src><(U16 *)>>bit_shift)&~leading_bit_mask;
                          src++;
                        }
                        for (i=0;i<whole_bytes;i++,src++,dst++)
                          *dst^=*src><(U16 *)>>bit_shift;
                        if (trailing_bits)
                          *dst=*dst&trailing_bit_mask|(*dst^*src><(U16 *)>>bit_shift)&~trailing_bit_mask;
                        kk +=img->width_internal;
                        kk1+=dc->width_internal;
                      }
                      break;
                }
                break;
              case DCT_MONO:
                nobound_switch (color.c0.rop) {
                  case ROPB_EQU:
                  case ROPB_CLEAR_MASK_EQU:
                    if (win_z_buf_ptr)
                      for (j=h1;j<h2;j++) {
                        src=img->body +kk>>3;
                        dst=dc->body+(kk1+plane1)>>3;
                        if (leading_bits) {
                          if (win_z_num>=*win_z_buf_ptr++)
                            *dst++=*dst&leading_bit_mask|*src><(U16 *)>>bit_shift&color_byte&~leading_bit_mask;
                          else
                            dst++;
                          src++;
                        }
                        for (i=0;i<whole_bytes;i++,src++,dst++)
                          if (win_z_num>=*win_z_buf_ptr++)
                            *dst=*src><(U16 *)>>bit_shift&color_byte;
                        if (trailing_bits && win_z_num>=*win_z_buf_ptr++)
                          *dst=*dst&trailing_bit_mask|*src><(U16 *)>>bit_shift&color_byte&~trailing_bit_mask;
                        kk +=img->width_internal;
                        kk1+=dc->width_internal;
                        if ((j+y)&7==7)
                          win_z_buf_ptr><(U8 *)+=win_z_buf_line_inc;
                        else
                          win_z_buf_ptr><(U8 *)-=win_z_buf_line_dec;
                      }
                      else
                      for (j=h2-h1;j;j--) {
                        src=img->body +kk>>3;
                        dst=dc->body+(kk1+plane1)>>3;
                        if (leading_bits) {
                          *dst++=*dst&leading_bit_mask|*src><(U16 *)>>bit_shift&color_byte&~leading_bit_mask;
                          src++;
                        }
                        for (i=0;i<whole_bytes;i++,src++,dst++)
                          *dst=*src><(U16 *)>>bit_shift&color_byte;
                        if (trailing_bits)
                          *dst=*dst&trailing_bit_mask|*src><(U16 *)>>bit_shift&color_byte&~trailing_bit_mask;
                        kk +=img->width_internal;
                        kk1+=dc->width_internal;
                      }
                      break;
                  case ROPB_CLEAR_MASK_TRANSPARENT:
                  case ROPB_TRANSPARENT:
                    if (win_z_buf_ptr)
                      for (j=h1;j<h2;j++) {
                        src=img->body +kk>>3;
                        dst=dc->body+(kk1+plane1)>>3;
                        if (!img->bkcolor.c0.color) {
                          if (leading_bits) {
                            if (win_z_num>=*win_z_buf_ptr++) {
                              m=*src><(U16 *)>>bit_shift;
                              *dst++=*dst&leading_bit_mask|(*dst&~m|m&color_byte)&~leading_bit_mask;
                            } else
                              dst++;
                            src++;
                          }
                          for (i=0;i<whole_bytes;i++,src++,dst++)
                            if (win_z_num>=*win_z_buf_ptr++) {
                              m=*src><(U16 *)>>bit_shift;
                              *dst=*dst&~m|m&color_byte;
                            }
                          if (trailing_bits && win_z_num>=*win_z_buf_ptr++) {
                            m=*src><(U16 *)>>bit_shift;
                            *dst=*dst&trailing_bit_mask|(*dst&~m|m&color_byte)&~trailing_bit_mask;
                          }
                        } else {
                          if (leading_bits) {
                            if (win_z_num>=*win_z_buf_ptr++) {
                              m=*src><(U16 *)>>bit_shift;
                              *dst++=*dst&leading_bit_mask|(*dst&m|~m&color_byte)&~leading_bit_mask;
                            } else
                              dst++;
                            src++;
                          }
                          for (i=0;i<whole_bytes;i++,src++,dst++)
                            if (win_z_num>=*win_z_buf_ptr++) {
                              m=*src><(U16 *)>>bit_shift;
                              *dst=*dst&m|~m&color_byte;
                            }
                          if (trailing_bits && win_z_num>=*win_z_buf_ptr++) {
                            m=*src><(U16 *)>>bit_shift;
                            *dst=*dst&trailing_bit_mask|(*dst&m|~m&color_byte)&~trailing_bit_mask;
                          }
                        }
                        kk +=img->width_internal;
                        kk1+=dc->width_internal;
                        if ((j+y)&7==7)
                          win_z_buf_ptr><(U8 *)+=win_z_buf_line_inc;
                        else
                          win_z_buf_ptr><(U8 *)-=win_z_buf_line_dec;
                      }
                      else
                      for (j=h2-h1;j;j--) {
                        src=img->body +kk>>3;
                        dst=dc->body+(kk1+plane1)>>3;
                        if (!img->bkcolor.c0.color) {
                          if (leading_bits) {
                            m=*src><(U16 *)>>bit_shift;
                            *dst++=*dst&leading_bit_mask|(*dst&~m|m&color_byte)&~leading_bit_mask;
                            src++;
                          }
                          for (i=0;i<whole_bytes;i++,src++,dst++) {
                            m=*src><(U16 *)>>bit_shift;
                            *dst=*dst&~m|m&color_byte;
                          }
                          if (trailing_bits) {
                            m=*src><(U16 *)>>bit_shift;
                            *dst=*dst&trailing_bit_mask|(*dst&~m|m&color_byte)&~trailing_bit_mask;
                          }
                        } else {
                          if (leading_bits) {
                            m=*src><(U16 *)>>bit_shift;
                            *dst++=*dst&leading_bit_mask|(*dst&m|~m&color_byte)&~leading_bit_mask;
                            src++;
                          }
                          for (i=0;i<whole_bytes;i++,src++,dst++) {
                            m=*src><(U16 *)>>bit_shift;
                            *dst=*dst&m|~m&color_byte;
                          }
                          if (trailing_bits) {
                            m=*src><(U16 *)>>bit_shift;
                            *dst=*dst&trailing_bit_mask|(*dst&m|~m&color_byte)&~trailing_bit_mask;
                          }
                        }
                        kk +=img->width_internal;
                        kk1+=dc->width_internal;
                      }
                      break;
                  case ROPB_XOR:
                    if (color_byte) {
                      if (win_z_buf_ptr)
                        for (j=h1;j<h2;j++) {
                          src=img->body +kk>>3;
                          dst=dc->body+(kk1+plane1)>>3;
                          if (leading_bits) {
                            if (win_z_num>=*win_z_buf_ptr++)
                              *dst++=*dst&leading_bit_mask|(*dst^*src><(U16 *)>>bit_shift)&~leading_bit_mask;
                            else
                              dst++;
                            src++;
                          }
                          for (i=0;i<whole_bytes;i++,src++,dst++)
                            if (win_z_num>=*win_z_buf_ptr++)
                              *dst^=*src><(U16 *)>>bit_shift;
                          if (trailing_bits && win_z_num>=*win_z_buf_ptr++)
                            *dst=*dst&trailing_bit_mask|(*dst^*src><(U16 *)>>bit_shift)&~trailing_bit_mask;
                          kk +=img->width_internal;
                          kk1+=dc->width_internal;
                          if ((j+y)&7==7)
                            win_z_buf_ptr><(U8 *)+=win_z_buf_line_inc;
                          else
                            win_z_buf_ptr><(U8 *)-=win_z_buf_line_dec;
                        }
                        else
                        for (j=h2-h1;j;j--) {
                          src=img->body +kk>>3;
                          dst=dc->body+(kk1+plane1)>>3;
                          if (leading_bits) {
                            *dst++=*dst&leading_bit_mask|(*dst^*src><(U16 *)>>bit_shift)&~leading_bit_mask;
                            src++;
                          }
                          for (i=0;i<whole_bytes;i++,src++,dst++)
                            *dst^=*src><(U16 *)>>bit_shift;
                          if (trailing_bits)
                            *dst=*dst&trailing_bit_mask|(*dst^*src><(U16 *)>>bit_shift)&~trailing_bit_mask;
                          kk +=img->width_internal;
                          kk1+=dc->width_internal;
                        }
                        }
                    break;
                }
                break;
            }
          }
          }
        break;
    }
    dc->depth_buf=db;
    if (mask=dc->mask) {
      if (color.c0.rop!=ROPB_COLLISION &&
          (!(color.c0.rop==ROPB_TRANSPARENT ||
          color.c0.rop==ROPB_CLEAR_MASK_TRANSPARENT) ||
          img->type==DCT_MONO)) {
        mask->brush=dc->brush;
        mask->color=WHITE;
        if (dc->color.c0.rop==ROPB_CLEAR_MASK_EQU)
          mask->color=ROPB_EQU+BLACK;
        else if (dc->color.c0.rop==ROPB_CLEAR_MASK_TRANSPARENT)
          mask->color=ROPB_TRANSPARENT+BLACK;
        else
          mask->color.c0.rop=dc->color.c0.rop;
        mask->win_task=dc->win_task;
        if (mask->alias)
          mask->alias->flags|=DCF_DIRTY;
        GrDCBlot(mask,_x,_y,img);
      }
    }
    return TRUE;
  } else
    return FALSE;
}

//This makes lots of assumptions.
U0 GrDCBlotWithMask(GrDC *dc,GrDC *img)
{
  U32 reg *mask_ptr,reg *m_dst,reg *m_src;
  U64 reg j,row,reg col,plane,
      d0=img->width_internal>>5;

  m_src=img->body;
  m_dst=dc->body;
  for (plane=1;plane<0x10;plane<<=1) {
    mask_ptr=img->mask->body;
    for (row=0;row<img->height;row++)
      for (col=0;col<d0;col++) {
        if (j=*mask_ptr)
          *m_dst=*m_dst&~j|*m_src&j;
        m_dst++;
        mask_ptr++;
        m_src++;
      }
  }
}

public GrDC *GrDCExtract(GrDC *dc,I64 x1,I64 y1,I64 x2,I64 y2,TaskStruct *task=NULL)
{
  GrDC *result;
  TaskStruct *win_task;
  if (x1>x2) SwapU64(&x1,&x2);
  if (y1>y2) SwapU64(&y1,&y2);
  if (dc->flags & DCF_SCREEN_BITMAP) {
    win_task=dc->win_task;
    x1+=win_task->win_pixel_left+win_task->win_scroll_x;
    y1+=win_task->win_pixel_top+win_task->win_scroll_y;
    x2+=win_task->win_pixel_left+win_task->win_scroll_x;
    y2+=win_task->win_pixel_top+win_task->win_scroll_y;
  }
  result=GrDCNew(dc->type,x2-x1+1,y2-y1+1,task);
  result->color=1;
  GrDCBlot(result,-x1,-y1,dc);
  result->color=0;
  return result;
}

#help_index "Graphics/Char;Char/Graphics"

public BoolI64 GrPutChar(GrDC *dc,I64 _x,I64 _y,U8 ch)
{  //Clipping but not transformation
  U8 reg *src,reg *dst,*font_ptr;
  I64 x=_x,y=_y,i,m,
      leading_bits,trailing_bits,leading_bit_mask,trailing_bit_mask,
      j,k1,kk1,w1,h1,w2,h2,plane1,reg bit_shift,p1,dist,
      reg color_byte,plane_size1,plane_limit1;
  ColorROPU32 color,c;
  GrDC *mask;
  TaskStruct *win_task;

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

  w1=x<0 ?-x:0;
  h1=y<0 ?-y:0;
  w2=FONT_WIDTH;
  h2=FONT_HEIGHT;

  if (dc->flags & DCF_SCREEN_BITMAP) {
    x+=win_task->win_pixel_left;
    y+=win_task->win_pixel_top;
  }
  if (dc->flags & DCF_LOCATE_NEAREST) { //TODO:Untested
    dist=DistI64(x+w2>>1,y+h2>>1,dc->cur_x,dc->cur_y);
    if (dist<=dc->nearest_dist) {
      dc->nearest_sprite_elem_num=dc->cur_sprite_elem_num;
      dc->nearest_dist=dist;
    }
  }
  if (dc->flags & DCF_SCREEN_BITMAP) {
    if (x+w1<0) w1=-x;
    if (x+w2>win_task->win_pixel_right+1)
      w2=win_task->win_pixel_right+1-x;

    if (y+h1<0) h1=-y;
    if (y+h2>win_task->win_pixel_bottom+1)
      h2=win_task->win_pixel_bottom+1-y;
  }
  if (x+w2>dc->width)
    w2=dc->width-x;
  if (y+h2>dc->height)
    h2=dc->height-y;
  if (w1<w2<=FONT_WIDTH && h1<h2<=FONT_HEIGHT) {
    if (dc->flags & DCF_RECORD_EXTENTS) {
      if (x+w1<dc->min_x) dc->min_x=x+w1;
      if (x+w2-1>dc->max_x) dc->max_x=x+w2-1;
      if (y+h1<dc->min_y) dc->min_y=y+h1;
      if (y+h2-1>dc->max_y) dc->max_y=y+h2-1;
    }
    if (dc->flags & DCF_DONT_DRAW)
      return TRUE;
    color=dc->color;
    plane_size1=dc->plane_size<<3;
    nobound_switch (dc->type) {
      case DCT_COLOR4:
        plane_limit1=plane_size1*4;
        break;
      case DCT_MONO:
        plane_limit1=plane_size1;
        break;
    }
    leading_bits=-(w1+x)&7;
    if (!leading_bits) leading_bits=8;
    leading_bit_mask=0xFF>>leading_bits;
    bit_shift=-x&7;
    trailing_bits=(x+w2)&7;
    trailing_bit_mask=0xFF<<trailing_bits;
    if (leading_bits+trailing_bits>w2-w1) {
      leading_bit_mask|=trailing_bit_mask;
      trailing_bits=0;
    }
    font_ptr=&gr_font[(FONT_WIDTH*(FONT_HEIGHT*ch+h1))>>3];
    if (color.c0.rop==ROPB_COLLISION) {
      m=w1%(FONT_WIDTH-1);
#assert FONT_WIDTH==8
      color =dc->bkcolor.c0.color;
      for (i=w1;i<w2;i++) {
        k1=(h1+y)*dc->width_internal+x;
        src=font_ptr;
        for (j=h2-h1;j;j--) {
          c=0;
          for (plane1=0,p1=1;plane1<plane_limit1;
          plane1+=plane_size1,p1<<=1)
            if (Bt(dc->body,k1+i+plane1))
              c+=p1;
          if (c!=color) {
            if (Bt(src,m))
              dc->collision_cnt++;
          }
          k1+=dc->width_internal;
          src++;
        }
        }
      } else {
      for (p1=0,plane1=0;plane1<plane_limit1;plane1+=plane_size1,p1++) {
        if (Bt(&color,p1))
          color_byte=-1;
        else
          color_byte=0;
        k1=x+w1;
        kk1=(h1+y)*dc->width_internal+k1+plane1;
        if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags&DCF_ON_TOP) {
          if (leading_bits) {
            dst=dc->body+kk1>>3;
            src=font_ptr;
            if (bit_shift)
              src--;
            nobound_switch (color.c0.rop) {
              case ROPB_EQU:
              case ROPB_CLEAR_MASK_EQU:
              case ROPB_CLEAR_MASK_TRANSPARENT:
              case ROPB_TRANSPARENT:
                for (j=h2-h1;j;j--) {
                  m=*src><(U16 *)>>bit_shift;
                  *dst=*dst&leading_bit_mask|(color_byte&m|*dst&~m)&~leading_bit_mask;
                  src++;
                  dst+=dc->width_internal>>3;
                }
                break;
              case ROPB_XOR:
                if (color_byte) {
                  for (j=h2-h1;j;j--) {
                    *dst=*dst&leading_bit_mask|(*dst^*src><(U16 *)>>bit_shift)&~leading_bit_mask;
                    src++;
                    dst+=dc->width_internal>>3;
                  }
                  }
                break;
            }
            kk1+=8;
          }
          if (trailing_bits) {
            dst=dc->body+kk1>>3;
            src=font_ptr+1;
            if (bit_shift)
              src--;
            nobound_switch (color.c0.rop) {
              case ROPB_EQU:
              case ROPB_CLEAR_MASK_EQU:
              case ROPB_CLEAR_MASK_TRANSPARENT:
              case ROPB_TRANSPARENT:
                for (j=h2-h1;j;j--) {
                  m=*src><(U16 *)>>bit_shift;
                  *dst=*dst&trailing_bit_mask|(color_byte&m|*dst&~m)&~trailing_bit_mask;
                  src++;
                  dst+=dc->width_internal>>3;
                }
                break;
              case ROPB_XOR:
                if (color_byte) {
                  for (j=h2-h1;j;j--) {
                    *dst=*dst&trailing_bit_mask|(*dst^*src><(U16 *)>>bit_shift)&~trailing_bit_mask;
                    src++;
                    dst+=dc->width_internal>>3;
                  }
                  }
                break;
            }
          }
        } else {
          if (leading_bits) {
            dst=dc->body+kk1>>3;
            src=font_ptr;
            if (bit_shift)
              src--;
            nobound_switch (color.c0.rop) {
              case ROPB_EQU:
              case ROPB_CLEAR_MASK_EQU:
              case ROPB_CLEAR_MASK_TRANSPARENT:
              case ROPB_TRANSPARENT:
                for (j=h1;j<h2;j++) {
                  if (!IsPixelCovered0(win_task,k1,y+j)) {
                    m=*src><(U16 *)>>bit_shift;
                    *dst=*dst&leading_bit_mask|(color_byte&m|*dst&~m)&~leading_bit_mask;
                  }
                  src++;
                  dst+=dc->width_internal>>3;
                }
                break;
              case ROPB_XOR:
                if (color_byte) {
                  for (j=h1;j<h2;j++) {
                    if (!IsPixelCovered0(win_task,k1,y+j))
                      *dst=*dst&leading_bit_mask|(*dst^*src><(U16 *)>>bit_shift)&~leading_bit_mask;
                    src++;
                    dst+=dc->width_internal>>3;
                  }
                  }
                break;
            }
            k1+=8;
            kk1+=8;
          }
          if (trailing_bits) {
            dst=dc->body+kk1>>3;
            src=font_ptr+1;
            if (bit_shift)
              src--;
            nobound_switch (color.c0.rop) {
              case ROPB_EQU:
              case ROPB_CLEAR_MASK_EQU:
              case ROPB_CLEAR_MASK_TRANSPARENT:
              case ROPB_TRANSPARENT:
                for (j=h1;j<h2;j++) {
                  if (!IsPixelCovered0(win_task,k1,y+j)) {
                    m=*src><(U16 *)>>bit_shift;
                    *dst=*dst&trailing_bit_mask|(color_byte&m|*dst&~m)&~trailing_bit_mask;
                  }
                  src++;
                  dst+=dc->width_internal>>3;
                }
                break;
              case ROPB_XOR:
                if (color_byte) {
                  for (j=h1;j<h2;j++) {
                    if (!IsPixelCovered0(win_task,k1,y+j))
                      *dst=*dst&trailing_bit_mask|(*dst^*src><(U16 *)>>bit_shift)&~trailing_bit_mask;
                    src++;
                    dst+=dc->width_internal>>3;
                  }
                  }
                break;
            }
          }
        }
      }
      }
    if (mask=dc->mask) {
      mask->brush=dc->brush;
      mask->color=WHITE;
      if (dc->color.c0.rop==ROPB_CLEAR_MASK_EQU)
        mask->color=ROPB_EQU+BLACK;
      else if (dc->color.c0.rop==ROPB_CLEAR_MASK_TRANSPARENT)
        mask->color=ROPB_TRANSPARENT+BLACK;
      else
        mask->color.c0.rop=dc->color.c0.rop;
      mask->win_task=dc->win_task;
      if (mask->alias)
        mask->alias->flags|=DCF_DIRTY;
      GrPutChar(mask,_x,_y,ch);
    }
    return TRUE;
  } else
    return FALSE;
}

public BoolI64 GrPutS(GrDC *dc,I64 _x,I64 _y,U8 *_s)
{
  I64 x0=_x,ch;
  BoolI8 result=FALSE,first=TRUE;
  if (!_s) return FALSE;
  while (ch=*_s++) {
    if (ch==CH_NEW_LINE) {
      _x=x0;
      _y+=FONT_HEIGHT;
    } else if (ch==CH_TAB)
      _x=x0+CeilU64(_x-x0+FONT_WIDTH,8*FONT_WIDTH);
    else {
      if (first) {
        result=GrPutChar(dc,_x,_y,ch);
        first=FALSE;
      } else
        GrPutChar(dc,_x,_y,ch);
      _x+=FONT_WIDTH;
    }
  }
  return result;
}

public BoolI64 GrVPutS(GrDC *dc,I64 _x,I64 _y,U8 *_s)
{ //Vertical text
  I64 y0=_y;
  U8 buf[2];
  BoolI8 result=FALSE,first=TRUE;
  if (!_s) return FALSE;
  buf[1]=0;
  while (*buf=*_s++) {
    if (*buf==CH_NEW_LINE) {
      _y=y0;
      _x+=FONT_WIDTH;
    } else if (*buf==CH_TAB)
      _y=y0+CeilU64(_y-y0+FONT_HEIGHT,8*FONT_HEIGHT);
    else {
      if (first) {
        result=GrPutS(dc,_x,_y,buf);
        first=FALSE;
      } else
        GrPutS(dc,_x,_y,buf);
      _y+=FONT_HEIGHT;
    }
  }
  return result;
}

public U0 GrPrintF(GrDC *dc,I64 x,I64 y,U8 *fmt,...)
{
  U8 *buf=SPrintFJoin(NULL,fmt,argc,argv);
  GrPutS(dc,x,y,buf);
  Free(buf);
}

public U0 GrVPrintF(GrDC *dc,I64 x,I64 y,U8 *fmt,...)
{ //Vertical text
  U8 *buf=SPrintFJoin(NULL,fmt,argc,argv);
  GrVPutS(dc,x,y,buf);
  Free(buf);
}

#help_index "Graphics"
public I64 GrRect(GrDC *dc,I64 _x,I64 _y,I64 w,I64 h)
{  //Clipping but not transformation
//Returns cnt of pixels changed
  I64 x=_x,y=_y,i,result=0,
      leading_bits,whole_bytes,trailing_bits,leading_bit_mask,trailing_bit_mask,
      j,k1,kk1,w1,h1,w2,h2,plane1,p,p1,dist,
      win_z_buf_line_inc,win_z_buf_line_dec,win_z_num;
  I64 plane_size1,plane_limit1;
  U8 reg *dst;
  U16 reg *win_z_buf_ptr;
  ColorROPU32 color,reg color_byte,c,dither_colors;
  BoolI8 dither,probability_dither;
  GrDC *mask;
  TaskStruct *win_task;

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

  w1=x<0 ?-x:0;
  h1=y<0 ?-y:0;
  w2=w;
  h2=h;

  if (dc->flags & DCF_SCREEN_BITMAP) {
    x+=win_task->win_pixel_left;
    y+=win_task->win_pixel_top;
  }
  if (dc->flags & DCF_LOCATE_NEAREST) { //TODO:Untested
    if (dc->cur_x>=x && dc->cur_y>=y &&
        dc->cur_x<=x+w && dc->cur_y<=y+h) {
      dc->nearest_sprite_elem_num=dc->cur_sprite_elem_num;
      dc->nearest_dist=0;
    } else {
      dist=DistI64(x+w>>1,y+h>>1,dc->cur_x,dc->cur_y);
      if (dist<=dc->nearest_dist) {
        dc->nearest_sprite_elem_num=dc->cur_sprite_elem_num;
        dc->nearest_dist=dist;
      }
    }
  }
  if (dc->flags & DCF_SCREEN_BITMAP) {
    if (x+w1<0) w1=-x;
    if (x+w2>win_task->win_pixel_right+1)
      w2=win_task->win_pixel_right+1-x;

    if (y+h1<0) h1=-y;
    if (y+h2>win_task->win_pixel_bottom+1)
      h2=win_task->win_pixel_bottom+1-y;
  }
  if (x+w2>dc->width)
    w2=dc->width-x;
  if (y+h2>dc->height)
    h2=dc->height-y;
  if (w1<w2<=w && h1<h2<=h) {
    if (dc->flags & DCF_RECORD_EXTENTS) {
      if (x+w1<dc->min_x) dc->min_x=x+w1;
      if (x+w2-1>dc->max_x) dc->max_x=x+w2-1;
      if (y+h1<dc->min_y) dc->min_y=y+h1;
      if (y+h2-1>dc->max_y) dc->max_y=y+h2-1;
    }
    if (dc->flags & DCF_DONT_DRAW)
      return TRUE;
    color=dc->color;
    if (color.c1.rop&(ROPBF_DITHER|ROPBF_PROBABILITY_DITHER)) {
      dither=TRUE;
      if (color.c1.rop&ROPBF_PROBABILITY_DITHER) {
        probability_dither=TRUE;
        color.c1.rop=color.c0.rop;
        dither_colors=color;
      } else {
        probability_dither=FALSE;
        color.c1.rop=color.c0.rop;
      }
    } else
      dither=FALSE;
    plane_size1=dc->plane_size<<3;
    nobound_switch (dc->type) {
      case DCT_COLOR4:
        plane_limit1=plane_size1*4;
        break;
      case DCT_MONO:
        plane_limit1=plane_size1;
        break;
    }
    leading_bits=-(w1+x)&7;
    leading_bit_mask=0xFF>>leading_bits;
    whole_bytes=(w2-w1-leading_bits)>>3;
    if (whole_bytes<0) whole_bytes=0;
    trailing_bits=(x+w2)&7;
    trailing_bit_mask=0xFF<<trailing_bits;
    if (leading_bits+trailing_bits>w2-w1) {
      leading_bit_mask|=trailing_bit_mask;
      leading_bits=w2-w1; //Correct so it's right for result.
      trailing_bits=0;
    }
    if (dither) {
      if (probability_dither) {
        if (Rand2U16<dc->dither_probability)
          color.c0=dither_colors.c1;
        else
          color.c0=dither_colors.c0;
      } else
        if (((x+w1)^(y+h1))&1)
        SwapU16(&color.c0,&color.c1);
      if (color.c0.rop==ROPB_COLLISION) {  //TODO: Might want to check win_z_buf
        color =dc->bkcolor.c0.color; //TODO: Is this dithered?
        k1=(h1+y)*dc->width_internal+x;
        result=-dc->collision_cnt;
        for (j=h2-h1;j;j--) {
          for (i=w1;i<w2;i++) {
            c=0;
            for (plane1=0,p=1;plane1<plane_limit1;
            plane1+=plane_size1,p<<=1)
              if (Bt(dc->body,k1+i+plane1))
                c+=p;
            if (c!=color)
              dc->collision_cnt++;
          }
          k1+=dc->width_internal;
        }
        result+=dc->collision_cnt;
      } else {
        for (p1=0,plane1=0;plane1<plane_limit1;
        plane1+=plane_size1,p1++) {
          if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags&DCF_ON_TOP)
            win_z_buf_ptr=NULL;
          else {
            win_z_num=win_task->win_z_num;
            win_z_buf_ptr=gr_win_z_buf><(U8 *)+((h1+y)/FONT_HEIGHT*TEXT_COLS+(w1+x)/FONT_WIDTH)*sizeof(U16);
            win_z_buf_line_dec=((leading_bits?1:0)+whole_bytes+(trailing_bits?1:0))*sizeof(U16);
            win_z_buf_line_inc=TEXT_COLS*sizeof(U16)-win_z_buf_line_dec;
          }
          kk1=(h1+y)*dc->width_internal+x+w1+plane1;
          nobound_switch (color.c0.rop) {
            case ROPB_EQU:
            case ROPB_CLEAR_MASK_EQU:
            case ROPB_CLEAR_MASK_TRANSPARENT:
            case ROPB_TRANSPARENT:
              if (win_z_buf_ptr) {
                result=0;
                for (j=h1;j<h2;j++) {
                  if (Bt(&color,p1))
                    color_byte=0x55;
                  else
                    color_byte=0;
                  dst=dc->body+kk1>>3;
                  if (leading_bits) {
                    if (win_z_num>=*win_z_buf_ptr++) {
                      *dst=*dst&leading_bit_mask|color_byte&~leading_bit_mask;
                      result+=leading_bits;
                    }
                    dst++;
                  }
                  for (i=0;i<whole_bytes;i++,dst++)
                    if (win_z_num>=*win_z_buf_ptr++) {
                      *dst=color_byte;
                      result+=8;
                    }
                  if (trailing_bits && win_z_num>=*win_z_buf_ptr++) {
                    *dst=*dst&trailing_bit_mask|color_byte&~trailing_bit_mask;
                    result+=trailing_bits;
                  }
                  if ((j+y)&7==7)
                    win_z_buf_ptr><(U8 *)+=win_z_buf_line_inc;
                  else
                    win_z_buf_ptr><(U8 *)-=win_z_buf_line_dec;
                  kk1+=dc->width_internal;
                  if (probability_dither) {
                    if (Rand2U16<dc->dither_probability)
                      color.c0=dither_colors.c1;
                    else
                      color.c0=dither_colors.c0;
                  } else
                    SwapU16(&color.c0,&color.c1);
                }
                } else {
                for (j=h2-h1;j;j--) {
                  if (Bt(&color,p1))
                    color_byte=0x55;
                  else
                    color_byte=0;
                  dst=dc->body+kk1>>3;
                  if (leading_bits)
                    *dst++=*dst&leading_bit_mask|color_byte&~leading_bit_mask;
                  for (i=0;i<whole_bytes;i++,dst++)
                    *dst=color_byte;
                  if (trailing_bits)
                    *dst=*dst&trailing_bit_mask|color_byte&~trailing_bit_mask;
                  kk1+=dc->width_internal;
                  if (probability_dither) {
                    if (Rand2U16<dc->dither_probability)
                      color.c0=dither_colors.c1;
                    else
                      color.c0=dither_colors.c0;
                  } else
                    SwapU16(&color.c0,&color.c1);
                }
                result=(h2-h1)*(w2-w1);
              }
              break;
            case ROPB_XOR:
              if (win_z_buf_ptr) {
                if (Bt(&color,p1))
                  result=0;
                for (j=h1;j<h2;j++) {
                  if (Bt(&color,p1)) {
                    color_byte=0x55;
                    dst=dc->body+kk1>>3;
                    if (leading_bits) {
                      if (win_z_num>=*win_z_buf_ptr++) {
                        *dst=*dst&leading_bit_mask^color_byte&~leading_bit_mask;
                        result+=leading_bits;
                      }
                      dst++;
                    }
                    for (i=0;i<whole_bytes;i++,dst++)
                      if (win_z_num>=*win_z_buf_ptr++) {
                        *dst^=color_byte;
                        result+=8;
                      }
                    if (trailing_bits && win_z_num>=*win_z_buf_ptr++) {
                      *dst=*dst&trailing_bit_mask^color_byte&~trailing_bit_mask;
                      result+=trailing_bits;
                    }
                  } else
                    win_z_buf_ptr><(U8 *)+=win_z_buf_line_dec;
                  if ((j+y)&7==7)
                    win_z_buf_ptr><(U8 *)+=win_z_buf_line_inc;
                  else
                    win_z_buf_ptr><(U8 *)-=win_z_buf_line_dec;
                  kk1+=dc->width_internal;
                  if (probability_dither) {
                    if (Rand2U16<dc->dither_probability)
                      color.c0=dither_colors.c1;
                    else
                      color.c0=dither_colors.c0;
                  } else
                    SwapU16(&color.c0,&color.c1);
                }
                } else {
                for (j=h2-h1;j;j--) {
                  if (Bt(&color,p1)) {
                    color_byte=0x55;
                    dst=dc->body+kk1>>3;
                    if (leading_bits)
                      *dst++=*dst&leading_bit_mask^color_byte&~leading_bit_mask;
                    for (i=0;i<whole_bytes;i++,dst++)
                      *dst^=color_byte;
                    if (trailing_bits)
                      *dst=*dst&trailing_bit_mask^color_byte&~trailing_bit_mask;
                  }
                  kk1+=dc->width_internal;
                  if (probability_dither) {
                    if (Rand2U16<dc->dither_probability)
                      color.c0=dither_colors.c1;
                    else
                      color.c0=dither_colors.c0;
                  } else
                    SwapU16(&color.c0,&color.c1);
                }
                result=(h2-h1)*(w2-w1);
              }
              break;
          }
          if (probability_dither) {
            if (Rand2U16<dc->dither_probability)
              color.c0=dither_colors.c1;
            else
              color.c0=dither_colors.c0;
          } else
            if ((h2-h1)&1)
            SwapU16(&color.c0,&color.c1);
        }
        color=dc->color;
        color.c1.rop=color.c0.rop;
        if (probability_dither) {
          if (Rand2U16<dc->dither_probability)
            color.c0=dither_colors.c1;
          else
            color.c0=dither_colors.c0;
        } else
          if (((x+w1+1)^(y+h1))&1)
          SwapU16(&color.c0,&color.c1);
        for (p1=0,plane1=0;plane1<plane_limit1;
        plane1+=plane_size1,p1++) {
          if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags&DCF_ON_TOP)
            win_z_buf_ptr=NULL;
          else {
            win_z_num=win_task->win_z_num;
            win_z_buf_ptr=gr_win_z_buf><(U8 *)+((h1+y)/FONT_HEIGHT*TEXT_COLS+(w1+x)/FONT_WIDTH)*sizeof(U16);
            win_z_buf_line_dec=((leading_bits?1:0)+whole_bytes+(trailing_bits?1:0))*sizeof(U16);
            win_z_buf_line_inc=TEXT_COLS*sizeof(U16)-win_z_buf_line_dec;
          }
          kk1=(h1+y)*dc->width_internal+x+w1+plane1;
          nobound_switch (color.c0.rop) {
            case ROPB_EQU:
            case ROPB_CLEAR_MASK_EQU:
            case ROPB_CLEAR_MASK_TRANSPARENT:
            case ROPB_TRANSPARENT:
              if (win_z_buf_ptr)
                for (j=h1;j<h2;j++) {
                  if (Bt(&color,p1))
                    color_byte=0xAA;
                  else
                    color_byte=0;
                  dst=dc->body+kk1>>3;
                  if (leading_bits) {
                    if (win_z_num>=*win_z_buf_ptr++)
                      *dst=*dst&leading_bit_mask|(*dst|color_byte)&~leading_bit_mask;
                    dst++;
                  }
                  for (i=0;i<whole_bytes;i++,dst++)
                    if (win_z_num>=*win_z_buf_ptr++)
                      *dst|=color_byte;
                  if (trailing_bits && win_z_num>=*win_z_buf_ptr++)
                    *dst=*dst&trailing_bit_mask|(*dst|color_byte)&~trailing_bit_mask;
                  if ((j+y)&7==7)
                    win_z_buf_ptr><(U8 *)+=win_z_buf_line_inc;
                  else
                    win_z_buf_ptr><(U8 *)-=win_z_buf_line_dec;
                  kk1+=dc->width_internal;
                  if (probability_dither) {
                    if (Rand2U16<dc->dither_probability)
                      color.c0=dither_colors.c1;
                    else
                      color.c0=dither_colors.c0;
                  } else
                    SwapU16(&color.c0,&color.c1);
                }
                else
                for (j=h2-h1;j;j--) {
                  if (Bt(&color,p1))
                    color_byte=0xAA;
                  else
                    color_byte=0;
                  dst=dc->body+kk1>>3;
                  if (leading_bits)
                    *dst++=*dst&leading_bit_mask|(*dst|color_byte)&~leading_bit_mask;
                  for (i=0;i<whole_bytes;i++,dst++)
                    *dst|=color_byte;
                  if (trailing_bits)
                    *dst=*dst&trailing_bit_mask|(*dst|color_byte)&~trailing_bit_mask;
                  kk1+=dc->width_internal;
                  if (probability_dither) {
                    if (Rand2U16<dc->dither_probability)
                      color.c0=dither_colors.c1;
                    else
                      color.c0=dither_colors.c0;
                  } else
                    SwapU16(&color.c0,&color.c1);
                }
                break;
            case ROPB_XOR:
              if (win_z_buf_ptr)
                for (j=h1;j<h2;j++) {
                  if (Bt(&color,p1)) {
                    color_byte=0xAA;
                    dst=dc->body+kk1>>3;
                    if (leading_bits) {
                      if (win_z_num>=*win_z_buf_ptr++)
                        *dst=*dst&leading_bit_mask^color_byte&~leading_bit_mask;
                      dst++;
                    }
                    for (i=0;i<whole_bytes;i++,dst++)
                      if (win_z_num>=*win_z_buf_ptr++)
                        *dst^=color_byte;
                    if (trailing_bits && win_z_num>=*win_z_buf_ptr++)
                      *dst=*dst&trailing_bit_mask^color_byte&~trailing_bit_mask;
                  } else
                    win_z_buf_ptr><(U8 *)+=win_z_buf_line_dec;
                  if ((j+y)&7==7)
                    win_z_buf_ptr><(U8 *)+=win_z_buf_line_inc;
                  else
                    win_z_buf_ptr><(U8 *)-=win_z_buf_line_dec;
                  kk1+=dc->width_internal;
                  if (probability_dither) {
                    if (Rand2U16<dc->dither_probability)
                      color.c0=dither_colors.c1;
                    else
                      color.c0=dither_colors.c0;
                  } else
                    SwapU16(&color.c0,&color.c1);
                }
                else
                for (j=h2-h1;j;j--) {
                  if (Bt(&color,p1)) {
                    color_byte=0xAA;
                    dst=dc->body+kk1>>3;
                    if (leading_bits)
                      *dst++=*dst&leading_bit_mask^color_byte&~leading_bit_mask;
                    for (i=0;i<whole_bytes;i++,dst++)
                      *dst^=color_byte;
                    if (trailing_bits)
                      *dst=*dst&trailing_bit_mask^color_byte&~trailing_bit_mask;
                  }
                  kk1+=dc->width_internal;
                  if (probability_dither) {
                    if (Rand2U16<dc->dither_probability)
                      color.c0=dither_colors.c1;
                    else
                      color.c0=dither_colors.c0;
                  } else
                    SwapU16(&color.c0,&color.c1);
                }
                break;
          }
          if (probability_dither) {
            if (Rand2U16<dc->dither_probability)
              color.c0=dither_colors.c1;
            else
              color.c0=dither_colors.c0;
          } else
            if ((h2-h1)&1)
            SwapU16(&color.c0,&color.c1);
        }
        }
    } else {
      if (color.c0.rop==ROPB_COLLISION) {  //TODO: Might want to check win_z_buf
        color =dc->bkcolor.c0.color;
        k1=(h1+y)*dc->width_internal+x;
        result=-dc->collision_cnt;
        for (j=h2-h1;j;j--) {
          for (i=w1;i<w2;i++) {
            c=0;
            for (plane1=0,p=1;plane1<plane_limit1;
            plane1+=plane_size1,p<<=1)
              if (Bt(dc->body,k1+i+plane1))
                c+=p;
            if (c!=color)
              dc->collision_cnt++;
          }
          k1+=dc->width_internal;
        }
        result+=dc->collision_cnt;
      } else {
        for (p1=0,plane1=0;plane1<plane_limit1;
        plane1+=plane_size1,p1++) {
          if (!(dc->flags & DCF_SCREEN_BITMAP) || dc->flags&DCF_ON_TOP)
            win_z_buf_ptr=NULL;
          else {
            win_z_num=win_task->win_z_num;
            win_z_buf_ptr=gr_win_z_buf><(U8 *)+((h1+y)/FONT_HEIGHT*TEXT_COLS+(w1+x)/FONT_WIDTH)*sizeof(U16);
            win_z_buf_line_dec=((leading_bits?1:0)+whole_bytes+(trailing_bits?1:0))*sizeof(U16);
            win_z_buf_line_inc=TEXT_COLS*sizeof(U16)-win_z_buf_line_dec;
          }
          if (Bt(&color,p1))
            color_byte=-1;
          else
            color_byte=0;
          kk1=(h1+y)*dc->width_internal+x+w1+plane1;
          nobound_switch (color.c0.rop) {
            case ROPB_EQU:
            case ROPB_CLEAR_MASK_EQU:
            case ROPB_CLEAR_MASK_TRANSPARENT:
            case ROPB_TRANSPARENT:
              if (win_z_buf_ptr) {
                result=0;
                for (j=h1;j<h2;j++) {
                  dst=dc->body+kk1>>3;
                  if (leading_bits) {
                    if (win_z_num>=*win_z_buf_ptr++) {
                      *dst=*dst&leading_bit_mask|color_byte&~leading_bit_mask;
                      result+=leading_bits;
                    }
                    dst++;
                  }
                  for (i=0;i<whole_bytes;i++,dst++)
                    if (win_z_num>=*win_z_buf_ptr++) {
                      *dst=color_byte;
                      result+=8;
                    }
                  if (trailing_bits && win_z_num>=*win_z_buf_ptr++) {
                    *dst=*dst&trailing_bit_mask|color_byte&~trailing_bit_mask;
                    result+=trailing_bits;
                  }
                  if ((j+y)&7==7)
                    win_z_buf_ptr><(U8 *)+=win_z_buf_line_inc;
                  else
                    win_z_buf_ptr><(U8 *)-=win_z_buf_line_dec;
                  kk1+=dc->width_internal;
                }
                } else {
                for (j=h2-h1;j;j--) {
                  dst=dc->body+kk1>>3;
                  if (leading_bits)
                    *dst++=*dst&leading_bit_mask|color_byte&~leading_bit_mask;
                  for (i=0;i<whole_bytes;i++,dst++)
                    *dst=color_byte;
                  if (trailing_bits)
                    *dst=*dst&trailing_bit_mask|color_byte&~trailing_bit_mask;
                  kk1+=dc->width_internal;
                }
                result=(h2-h1)*(w2-w1);
              }
              break;
            case ROPB_XOR:
              if (color_byte) {
                if (win_z_buf_ptr) {
                  result=0;
                  for (j=h1;j<h2;j++) {
                    dst=dc->body+kk1>>3;
                    if (leading_bits) {
                      if (win_z_num>=*win_z_buf_ptr++) {
                        *dst=*dst&leading_bit_mask^~leading_bit_mask;
                        result+=leading_bits;
                      }
                      dst++;
                    }
                    for (i=0;i<whole_bytes;i++,dst++)
                      if (win_z_num>=*win_z_buf_ptr++) {
                        *dst^=0xFF;
                        result+=8;
                      }
                    if (trailing_bits && win_z_num>=*win_z_buf_ptr++) {
                      *dst=*dst&trailing_bit_mask^~trailing_bit_mask;
                      result+=trailing_bits;
                    }
                    if ((j+y)&7==7)
                      win_z_buf_ptr><(U8 *)+=win_z_buf_line_inc;
                    else
                      win_z_buf_ptr><(U8 *)-=win_z_buf_line_dec;
                    kk1+=dc->width_internal;
                  }
                  } else {
                  for (j=h2-h1;j;j--) {
                    dst=dc->body+kk1>>3;
                    if (leading_bits)
                      *dst++=*dst&leading_bit_mask^~leading_bit_mask;
                    for (i=0;i<whole_bytes;i++,dst++)
                      *dst^=0xFF;
                    if (trailing_bits)
                      *dst=*dst&trailing_bit_mask^~trailing_bit_mask;
                    kk1+=dc->width_internal;
                  }
                  result=(h2-h1)*(w2-w1);
                }
              }
              break;
          }
        }
        }
    }
    if (mask=dc->mask) {
      mask->brush=dc->brush;
      mask->color=WHITE;
      if (dc->color.c0.rop==ROPB_CLEAR_MASK_EQU)
        mask->color=ROPB_EQU+BLACK;
      else if (dc->color.c0.rop==ROPB_CLEAR_MASK_TRANSPARENT)
        mask->color=ROPB_TRANSPARENT+BLACK;
      else
        mask->color.c0.rop=dc->color.c0.rop;
      mask->win_task=dc->win_task;
      if (mask->alias)
        mask->alias->flags|=DCF_DIRTY;
      GrRect(mask,_x,_y,w,h);
    }
  }
  return result;
}


I64 GrRayLenMinus(GrDC *dc,I64 x,I64 y)
{
//Returns cnt of pixels changed
  I64 result=0,c,x3,y3,d,bit;
  U8 *dst,*dst2,*mask_dst;
  BoolI8 not_color=ToBool(dc->flags&DCF_FILL_NOT_COLOR);
  GrDC *mask;
  TaskStruct *win_task;

  if (dc->flags & DCF_SCREEN_BITMAP) {
    win_task=dc->win_task;
    x+=win_task->win_scroll_x;
    y+=win_task->win_scroll_y;
  }
  x3=x;
  y3=y;
  if (x3<0 || y3<0)
    goto gr_done;
  if (dc->flags & DCF_SCREEN_BITMAP) {
    x3+=win_task->win_pixel_left;
    y3+=win_task->win_pixel_top;
    if (x3<0 || y3<0 ||
        x3>win_task->win_pixel_right ||
        y3>win_task->win_pixel_bottom ||
        !(dc->flags&DCF_ON_TOP) &&
          IsPixelCovered0(win_task,x3,y3))
      goto gr_done;
  }
  if (x3>=dc->width || y3>=dc->height)
    goto gr_done;

  d=y3*dc->width_internal;
  nobound_switch (dc->type) {
    case DCT_COLOR4:
      if (mask=dc->mask)
        mask_dst=mask->body-dc->body;
      else
        mask_dst=NULL;
      dst2=dc->body+d>>3;
      d=dc->plane_size;

      while (TRUE) {
        x3=x;
        if (x3&(FONT_WIDTH-1)==FONT_WIDTH-1) {
          if (dc->flags & DCF_SCREEN_BITMAP) {
            if (x3<0) break;
            x3+=win_task->win_pixel_left;
            if (x3<0 || x3>win_task->win_pixel_right ||
                x3>=dc->width ||
                !(dc->flags&DCF_ON_TOP) &&
                  IsPixelCovered0(win_task,x3,y3))
             break;
          } else
            if (!(0<=x3<dc->width))
              break;
        } else if (dc->flags & DCF_SCREEN_BITMAP)
          x3+=win_task->win_pixel_left;

        dst=dst2+x3>>3;
        bit=1<<(x3&7);

        if (mask_dst)
          if (!(mask_dst[dst]&bit)) {
            c=MAX_I32;
            goto here1a;
          }

        c=0;
        if (*dst & bit)
          c|=1;
        dst+=d;
        if (*dst & bit)
          c|=2;
        dst+=d;
        if (*dst & bit)
          c|=4;
        dst+=d;
        if (*dst & bit)
          c|=8;

here1a:
        if (not_color) {
          if (c!=dc->color2) {
            result++;
            x--;
          } else
            break;
        } else {
          if (c==dc->color2) {
            result++;
            x--;
          } else
            break;
        }
      }
      return result;
    case DCT_MONO:
      while (TRUE) {
        x3=x;
        if (x3&(FONT_WIDTH-1)==FONT_WIDTH-1) {
          if (dc->flags & DCF_SCREEN_BITMAP) {
            if (x3<0) break;
            x3+=win_task->win_pixel_left;
            if (x3<0 || x3>win_task->win_pixel_right ||
                x3>=dc->width ||
                !(dc->flags&DCF_ON_TOP) &&
                IsPixelCovered0(win_task,x3,y3))
              break;
          } else
            if (!(0<=x3<dc->width))
              break;
        } else if (dc->flags & DCF_SCREEN_BITMAP)
          x3+=win_task->win_pixel_left;
        c=1;
        if (mask=dc->mask)
          c=Bt(mask->body,d+x3);
        if (not_color) {
          if (!c || Bt(dc->body,d+x3)!=dc->color2) {
            result++;
            x--;
          } else
            break;
        } else {
          if (c && Bt(dc->body,d+x3)==dc->color2) {
            result++;
            x--;
          } else
            break;
        }
      }
      return result;
  }
gr_done:
return 0;
}

I64 GrRayLen(GrDC *dc,I64 *x1,I64 y,I64 z=0,I32 *db=NULL)
{
//Returns cnt of pixels changed
  I64 result=0,d,x=*x1,x2,x3,y3,dist,bit,not_bit;
  BoolI8 dither,probability_dither,not_color=ToBool(dc->flags&DCF_FILL_NOT_COLOR);
  U8 *dst,*dst2,*mask_dst;
  ColorROPU32 c,c2,color=dc->color,bkcolor=dc->bkcolor;
  I32 *db2;
  BoolI8 plot;
  GrDC *mask;
  TaskStruct *win_task;

  if (dc->flags & DCF_SCREEN_BITMAP) {
    win_task=dc->win_task;
    x+=win_task->win_scroll_x;
    y+=win_task->win_scroll_y;
    z+=win_task->win_scroll_z;
  }
  x2=x;
  x3=x;
  y3=y;
  if (x3<0 || y3<0)
    goto gr_done;
  if (dc->flags & DCF_SCREEN_BITMAP) {
    x3+=win_task->win_pixel_left;
    y3+=win_task->win_pixel_top;
    if (x3<0 || y3<0 ||
        x3>win_task->win_pixel_right ||
        y3>win_task->win_pixel_bottom ||
        !(dc->flags&DCF_ON_TOP) &&
        IsPixelCovered0(win_task,x3,y3))
      goto gr_done;
  }
  if (x3>=dc->width || y3>=dc->height)
    goto gr_done;

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

  color=dc->color;
  if (color.c1.rop&(ROPBF_DITHER|ROPBF_PROBABILITY_DITHER)) {
    dither=TRUE;
    if (color.c1.rop&ROPBF_PROBABILITY_DITHER) {
      probability_dither=TRUE;
      color.c1.rop=color.c0.rop;
    } else {
      probability_dither=FALSE;
      color.c1.rop=color.c0.rop;
      if (color.c0.color==dc->color2.c0.color)
        SwapU16(&color.c0,&color.c1);
    }
  } else
    dither=FALSE;
  nobound_switch (dc->type) {
    case DCT_COLOR4:
      if (mask=dc->mask) {
        mask_dst=mask->body-dc->body;
        if (mask->alias)
          mask->alias->flags|=DCF_DIRTY;
      } else
        mask_dst=NULL;
      dst2=dc->body+d>>3;
      d=dc->plane_size;
      while (TRUE) {
        x3=x;
        if (!(x3&(FONT_WIDTH-1))) {
          if (dc->flags & DCF_SCREEN_BITMAP) {
            if (x3<0) break;
            x3+=win_task->win_pixel_left;
            if (x3<0 || x3>win_task->win_pixel_right ||
                x3>=dc->width ||
                !(dc->flags&DCF_ON_TOP) &&
                IsPixelCovered0(win_task,x3,y3))
              break;
          } else {
            if (!(0<=x3<dc->width))
              break;
          }
        } else if (dc->flags & DCF_SCREEN_BITMAP)
          x3+=win_task->win_pixel_left;

        dst=dst2+x3>>3;
        bit=1<<(x3&7);
        not_bit=~bit;

        if (mask_dst)
          if (!(mask_dst[dst]&bit)) {
            c=MAX_I32;
            goto here3a;
          }

        c=0;
        if (*dst & bit)
          c|=1;
        dst+=d;
        if (*dst & bit)
          c|=2;
        dst+=d;
        if (*dst & bit)
          c|=4;
        dst+=d;
        if (*dst & bit)
          c|=8;

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

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

          c=color.c0.color;
          if (dither) {
            if (probability_dither) {
              if (Rand2U16<dc->dither_probability)
                c=color.c1.color;
            } else
              if ((x3^y3)&1)
                c=color.c1.color;
          }
          nobound_switch (color.c0.rop) {
            case ROPB_CLEAR_MASK_EQU:
            case ROPB_CLEAR_MASK_TRANSPARENT:
              if (mask_dst)
                mask_dst[dst]&=not_bit;
              goto here1a;
            case ROPB_EQU:
            case ROPB_TRANSPARENT:
              if (mask_dst)
                mask_dst[dst]|=bit;
here1a:
              if (c & 1)
                *dst|=bit;
              else
                *dst&=not_bit;
              dst+=d;
              if (c & 2)
                *dst|=bit;
              else
                *dst&=not_bit;
              dst+=d;
              if (c & 4)
                *dst|=bit;
              else
                *dst&=not_bit;
              dst+=d;
              if (c & 8)
                *dst|=bit;
              else
                *dst&=not_bit;
              break;
            case ROPB_COLLISION:
              c2=0;
              if (*dst&bit)
                c2+=1;
              dst+=d;
              if (*dst&bit)
                c2+=2;
              dst+=d;
              if (*dst&bit)
                c2+=4;
              dst+=d;
              if (*dst&bit)
                c2+=8;
              if (c2!=bkcolor.c0.color)
                dc->collision_cnt++;
              break;
            case ROPB_XOR:
              if (mask_dst)
                mask_dst[dst]|=bit;
              if (c & 1) *dst^=bit;
              dst+=d;
              if (c & 2) *dst^=bit;
              dst+=d;
              if (c & 4) *dst^=bit;
              dst+=d;
              if (c & 8) *dst^=bit;
              break;
          }
          result++;
          x++;
        } else
          break;
      }
      if (dc->flags & DCF_SCREEN_BITMAP)
        *x1=x-1-win_task->win_scroll_x;
      else
        *x1=x-1;
      x=x2-1;
      while (TRUE) {
        x3=x;
        if (x3&(FONT_WIDTH-1)==FONT_WIDTH-1) {
          if (dc->flags & DCF_SCREEN_BITMAP) {
            if (x3<0) break;
            x3+=win_task->win_pixel_left;
            if (x3<0 || x3>win_task->win_pixel_right ||
                x3>=dc->width ||
                !(dc->flags&DCF_ON_TOP) &&
                IsPixelCovered0(win_task,x3,y3))
              break;
          } else
            if (!(0<=x3<dc->width))
            break;
        } else if (dc->flags & DCF_SCREEN_BITMAP)
          x3+=win_task->win_pixel_left;

        dst=dst2+x3>>3;
        bit=1<<(x3&7);
        not_bit=~bit;

        if (mask_dst)
          if (!(mask_dst[dst]&bit)) {
            c=MAX_I32;
            goto here4a;
          }

        c=0;
        if (*dst & bit)
          c|=1;
        dst+=d;
        if (*dst & bit)
          c|=2;
        dst+=d;
        if (*dst & bit)
          c|=4;
        dst+=d;
        if (*dst & bit)
          c|=8;

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

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

          c=color.c0.color;
          if (dither) {
            if (probability_dither) {
              if (Rand2U16<dc->dither_probability)
                c=color.c1.color;
            } else
              if ((x3^y3)&1)
                c=color.c1.color;
          }
          nobound_switch (color.c0.rop) {
            case ROPB_CLEAR_MASK_EQU:
            case ROPB_CLEAR_MASK_TRANSPARENT:
              if (mask_dst)
                mask_dst[dst]&=not_bit;
              goto here2a;
            case ROPB_EQU:
            case ROPB_TRANSPARENT:
              if (mask_dst)
                mask_dst[dst]|=bit;
here2a:
              if (c & 1)
                *dst|=bit;
              else
                *dst&=not_bit;
              dst+=d;
              if (c & 2)
                *dst|=bit;
              else
                *dst&=not_bit;
              dst+=d;
              if (c & 4)
                *dst|=bit;
              else
                *dst&=not_bit;
              dst+=d;
              if (c & 8)
                *dst|=bit;
              else
                *dst&=not_bit;
              break;
            case ROPB_COLLISION:
              c2=0;
              if (*dst&bit)
                c2+=1;
              dst+=d;
              if (*dst&bit)
                c2+=2;
              dst+=d;
              if (*dst&bit)
                c2+=4;
              dst+=d;
              if (*dst&bit)
                c2+=8;
              if (c2!=bkcolor.c0.color)
                dc->collision_cnt++;
              break;
            case ROPB_XOR:
              if (mask_dst)
                mask_dst[dst]|=bit;
              if (c & 1) *dst^=bit;
              dst+=d;
              if (c & 2) *dst^=bit;
              dst+=d;
              if (c & 4) *dst^=bit;
              dst+=d;
              if (c & 8) *dst^=bit;
              break;
          }
          result++;
          x--;
        } else
          break;
      }
      return result;
    case DCT_MONO:
      if (mask=dc->mask)
        if (mask->alias)
          mask->alias->flags|=DCF_DIRTY;
      while (TRUE) {
        x3=x;
        if (!(x3&(FONT_WIDTH-1))) {
          if (dc->flags & DCF_SCREEN_BITMAP) {
            if (x3<0) break;
            x3+=win_task->win_pixel_left;
            if (x3<0 || x3>win_task->win_pixel_right ||
                x3>=dc->width ||
                !(dc->flags&DCF_ON_TOP) &&
                IsPixelCovered0(win_task,x3,y3))
              break;
          } else
            if (!(0<=x3<dc->width))
            break;
        } else if (dc->flags & DCF_SCREEN_BITMAP)
          x3+=win_task->win_pixel_left;
        c=Bt(dc->body,d+x3);
        if (mask)
          if (!Bt(mask->body,d+x3))
            c=MAX_I32;

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

        if ((not_color && c!=dc->color2 ||
            !not_color && c==dc->color2) && plot) {
          if (dc->flags & DCF_LOCATE_NEAREST) {
            dist=DistI64(x3,y3,dc->cur_x,dc->cur_y);
            if (dist<=dc->nearest_dist) {
              dc->nearest_sprite_elem_num=dc->cur_sprite_elem_num;
              dc->nearest_dist=dist;
            }
          }
          if (dc->flags & DCF_RECORD_EXTENTS) {
            if (x3<dc->min_x) dc->min_x=x3;
            if (x3>dc->max_x) dc->max_x=x3;
            if (y3<dc->min_y) dc->min_y=y3;
            if (y3>dc->max_y) dc->max_y=y3;
          }
          c=color.c0.color;
          if (dither) {
            if (probability_dither) {
              if (Rand2U16<dc->dither_probability)
                c=color.c1.color;
            } else
              if ((x3^y3)&1)
                c=color.c1.color;
          }
          nobound_switch (color.c0.rop) {
            case ROPB_CLEAR_MASK_EQU:
            case ROPB_CLEAR_MASK_TRANSPARENT:
              BEqu(dc->body,d+x3,c);
              if (mask)
                Btr(mask->body,d+x3);
              break;
            case ROPB_EQU:
            case ROPB_TRANSPARENT:
              BEqu(dc->body,d+x3,c);
              if (mask)
                Bts(mask->body,d+x3);
              break;
            case ROPB_COLLISION:
              if (c) {
                if (bkcolor&COLORROP_COLORS_MASK)
                  dc->collision_cnt+=Bt(dc->body,d+x3);
                else
                  dc->collision_cnt+=!Bt(dc->body,d+x3);
              } else {
                if (bkcolor&COLORROP_COLORS_MASK)
                  dc->collision_cnt+=!Bt(dc->body,d+x3);
                else
                  dc->collision_cnt+=Bt(dc->body,d+x3);
              }
              break;
            case ROPB_XOR:
              if (c)
                Btc(dc->body,d+x3);
              if (mask)
                Bts(mask->body,d+x3);
              break;
          }
          result++;
          x++;
        } else
          break;
      }
      if (dc->flags & DCF_SCREEN_BITMAP)
        *x1=x-1-win_task->win_scroll_x;
      else
        *x1=x-1;
      x=x2-1;
      while (TRUE) {
        x3=x;
        if (x3&(FONT_WIDTH-1)==FONT_WIDTH-1) {
          if (dc->flags & DCF_SCREEN_BITMAP) {
            if (x3<0) break;
            x3+=win_task->win_pixel_left;
            if (x3<0 || x3>win_task->win_pixel_right ||
                x3>=dc->width ||
                !(dc->flags&DCF_ON_TOP) &&
                IsPixelCovered0(win_task,x3,y3))
              break;
          } else
            if (!(0<=x3<dc->width) )
            break;
        } else if (dc->flags & DCF_SCREEN_BITMAP)
          x3+=win_task->win_pixel_left;

        c=Bt(dc->body,d+x3);
        if (mask)
          if (!Bt(mask->body,d+x3))
            c=MAX_I32;

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

        if ((not_color && c!=dc->color2 ||
            !not_color && c==dc->color2) && plot) {
          if (dc->flags & DCF_LOCATE_NEAREST) {
            dist=DistI64(x3,y3,dc->cur_x,dc->cur_y);
            if (dist<=dc->nearest_dist) {
              dc->nearest_sprite_elem_num=dc->cur_sprite_elem_num;
              dc->nearest_dist=dist;
            }
          }
          if (dc->flags & DCF_RECORD_EXTENTS) {
            if (x3<dc->min_x) dc->min_x=x3;
            if (x3>dc->max_x) dc->max_x=x3;
            if (y3<dc->min_y) dc->min_y=y3;
            if (y3>dc->max_y) dc->max_y=y3;
          }
          c=color.c0.color;
          if (dither) {
            if (probability_dither) {
              if (Rand2U16<dc->dither_probability)
                c=color.c1.color;
            } else
              if ((x3^y3)&1)
                c=color.c1.color;
          }
          nobound_switch (color.c0.rop) {
            case ROPB_CLEAR_MASK_EQU:
            case ROPB_CLEAR_MASK_TRANSPARENT:
              BEqu(dc->body,d+x3,c);
              if (mask)
                Btr(mask->body,d+x3);
              break;
            case ROPB_EQU:
            case ROPB_TRANSPARENT:
              BEqu(dc->body,d+x3,c);
              if (mask)
                Bts(mask->body,d+x3);
              break;
            case ROPB_COLLISION:
              if (c) {
                if (bkcolor&COLORROP_COLORS_MASK)
                  dc->collision_cnt+=Bt(dc->body,d+x3);
                else
                  dc->collision_cnt+=!Bt(dc->body,d+x3);
              } else {
                if (bkcolor&COLORROP_COLORS_MASK)
                  dc->collision_cnt+=!Bt(dc->body,d+x3);
                else
                  dc->collision_cnt+=Bt(dc->body,d+x3);
              }
              break;
            case ROPB_XOR:
              if (c)
                Btc(dc->body,d+x3);
              if (mask)
                Bts(mask->body,d+x3);
              break;
          }
          result++;
          x--;
        } else
          break;
      }
      return result;
  }
gr_done:
return 0;
}

public I64 GrHLine(GrDC *dc,I64 x1,I64 x2,I64 y,I64 z1=0,I64 z2=0)
{  //No transformation or pen width
//returns cnt of pixels changed
//This uses fixed-point.
  I64 dx,dz,z,result=0,i,j,_x1=x1,_x2=x2,_y=y,_z1=z1,_z2=z2,dist;
  U64 d,dd,bit,not_bit;
  U8 *dst;
  ColorROPU32 c,c2,color=dc->color,bkcolor=dc->bkcolor,dither_colors;
  I32 *db;
  BoolI8 plot=TRUE,char_clear,dither,probability_dither;
  GrDC *mask;
  TaskStruct *win_task;

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

  if (dc->flags & DCF_SCREEN_BITMAP) {
    win_task=dc->win_task;
    x1+=win_task->win_scroll_x;
    x2+=win_task->win_scroll_x;
    y +=win_task->win_scroll_y;
    z1+=win_task->win_scroll_z;
    z2+=win_task->win_scroll_z;
  }
  if (dc->flags & DCF_RECORD_EXTENTS) {
    if (x1<dc->min_x) dc->min_x=x1;
    if (x1>dc->max_x) dc->max_x=x1;
    if (x2<dc->min_x) dc->min_x=x2;
    if (x2>dc->max_x) dc->max_x=x2;
    if (y<dc->min_y) dc->min_y=y;
    if (y>dc->max_y) dc->max_y=y;
  }
  if (y<0) goto gr_done;
  if (x2<x1) {
    SwapU64(&x1,&x2);
    SwapU64(&z1,&z2);
  }
  if (x2<0)
    goto gr_done;
  if (x1<0) {
    i=-x1;
    x1=0;
  } else
    i=0;
  j=0;
  if (dc->flags & DCF_SCREEN_BITMAP) {
    x1+=win_task->win_pixel_left;
    x2+=win_task->win_pixel_left;
    if (x1>win_task->win_pixel_right)
      goto gr_done;
    if (x2>win_task->win_pixel_right) {
      j=x2-win_task->win_pixel_right;
      x2=win_task->win_pixel_right;
    }
    y+=win_task->win_pixel_top;
    if (y>win_task->win_pixel_bottom ||
        y<0 ||
        x2<0)
      goto gr_done;
  }
  if (x1>=dc->width || y>=dc->height)
    goto gr_done;
  dx=x2+j-(x1-i);
  d=dc->width_internal*y+x1;
  if (db=dc->depth_buf) {
    db+=d;
    if (dx)
      dz=(z2-z1)<<32/dx;
    else
      dz=0;
    z=z1<<32;
  }
  if (i)
    z+=i*dz;
  if (x2>=dc->width)
    x2=dc->width-1;

  dd=dc->plane_size;

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

  if (!(dc->flags & DCF_SCREEN_BITMAP) ||
      win_task->next_task==sys_winmgr_task ||
      dc->flags&DCF_ON_TOP ||
      !IsPixelCovered0(win_task,x1,y))
    char_clear=TRUE;
  else
    char_clear=FALSE;
  if (color.c1.rop&(ROPBF_DITHER|ROPBF_PROBABILITY_DITHER)) {
    dither=TRUE;
    if (color.c1.rop&ROPBF_PROBABILITY_DITHER) {
      probability_dither=TRUE;
      color.c1.rop=color.c0.rop;
      dither_colors=color;
      if (Rand2U16<dc->dither_probability)
        color.c0=dither_colors.c1;
      else
        color.c0=dither_colors.c0;
    } else {
      probability_dither=FALSE;
      color.c1.rop=color.c0.rop;
      if ((x1^y)&1)
        SwapU16(&color.c0,&color.c1);
    }
  } else
    dither=FALSE;
  while (x1<=x2) {
    if (char_clear) {
      if (db) {
        if (0<=z.i32[1]<=*db) {
          *db=z.i32[1];
          plot=TRUE;
        } else
          plot=FALSE;
      }
      if (plot) {
        nobound_switch (dc->type) {
          case DCT_COLOR4:
            dst=dc->body+d>>3;
            bit=1<<(x1&7);
            not_bit=~bit;
            c=color.c0.color;
            nobound_switch (color.c0.rop) {
              case ROPB_EQU:
              case ROPB_CLEAR_MASK_EQU:
              case ROPB_CLEAR_MASK_TRANSPARENT:
              case ROPB_TRANSPARENT:
                if (c & 1)
                  *dst|=bit;
                else
                  *dst&=not_bit;
                dst+=dd;
                if (c & 2)
                  *dst|=bit;
                else
                  *dst&=not_bit;
                dst+=dd;
                if (c & 4)
                  *dst|=bit;
                else
                  *dst&=not_bit;
                dst+=dd;
                if (c & 8)
                  *dst|=bit;
                else
                  *dst&=not_bit;
                break;
              case ROPB_COLLISION:
                c2=0;
                if (*dst&bit)
                  c2+=1;
                dst+=dd;
                if (*dst&bit)
                  c2+=2;
                dst+=dd;
                if (*dst&bit)
                  c2+=4;
                dst+=dd;
                if (*dst&bit)
                  c2+=8;
                if (c2!=bkcolor.c0.color)
                  dc->collision_cnt++;
                break;
              case ROPB_XOR:
                if (c & 1) *dst^=bit;
                dst+=dd;
                if (c & 2) *dst^=bit;
                dst+=dd;
                if (c & 4) *dst^=bit;
                dst+=dd;
                if (c & 8) *dst^=bit;
                break;
            }
            break;
          case DCT_MONO:
            c=color.c0.color;
            nobound_switch (color.c0.rop) {
              case ROPB_EQU:
              case ROPB_CLEAR_MASK_EQU:
              case ROPB_CLEAR_MASK_TRANSPARENT:
              case ROPB_TRANSPARENT:
                BEqu(dc->body,d,c);
                break;
              case ROPB_COLLISION:
                if (c) {
                  if (bkcolor&COLORROP_COLORS_MASK)
                    dc->collision_cnt+=Bt(dc->body,d);
                  else
                    dc->collision_cnt+=!Bt(dc->body,d);
                } else {
                  if (bkcolor&COLORROP_COLORS_MASK)
                    dc->collision_cnt+=!Bt(dc->body,d);
                  else
                    dc->collision_cnt+=Bt(dc->body,d);
                }
                break;
              case ROPB_XOR:
                if (c)
                  Btc(dc->body,d);
                break;
            }
            break;
        }
        result++;
      }
    }
    if (dither) {
      if (probability_dither) {
        if (Rand2U16<dc->dither_probability)
          color.c0=dither_colors.c1;
        else
          color.c0=dither_colors.c0;
      } else
        SwapU16(&color.c0,&color.c1);
    }
    d++;
    x1++;
    if (db)
      db++;
    z+=dz;
    if (!(x1&(FONT_WIDTH-1)) && x1<=x2) {
      if (!(dc->flags & DCF_SCREEN_BITMAP)||
          win_task->next_task==sys_winmgr_task ||
          dc->flags&DCF_ON_TOP ||
          !IsPixelCovered0(win_task,x1,y))
        char_clear=TRUE;
      else
        char_clear=FALSE;
    }
  }
  if (mask=dc->mask) {
    mask->brush=dc->brush;
    mask->color=WHITE;
    if (dc->color.c0.rop==ROPB_CLEAR_MASK_EQU)
      mask->color=ROPB_EQU+BLACK;
    else if (dc->color.c0.rop==ROPB_CLEAR_MASK_TRANSPARENT)
      mask->color=ROPB_TRANSPARENT+BLACK;
    else
      mask->color.c0.rop=dc->color.c0.rop;
    mask->win_task=dc->win_task;
    if (mask->alias)
      mask->alias->flags|=DCF_DIRTY;
    GrHLine(mask,_x1,_x2,_y,_z1,_z2);
  }
gr_done:
return result;
}
Historical capture: 2012-03-02. Home | Power on LoseThos | Sources and changes