#define BLACK                0
#define BLUE                1
#define GREEN                2
#define CYAN                3
#define RED                4
#define PURPLE                5
#define BROWN                6
#define LTGRAY                7
#define DKGRAY                8
#define LTBLUE                9
#define LTGREEN                10
#define LTCYAN                11
#define LTRED                12
#define LTPURPLE        13
#define YELLOW                14
#define WHITE                15


//Low 8 bits reserved for flags that go into saved bitmaps
#define BMF_COMPRESSED                1


#define BMF_TRANSFORMATION        0x100

//See $LK,"GrSetSymmetry","MN:GrSetSymmetry"$() or $LK,"GrSetSymmetry3","MN:GrSetSymmetry3"$()
#define BMF_SYMMETRY                0x200

//Must be used with BMF_SYMMETRY set also.
//See $LK,"WarGame","FF:::/LT/Apps/WarGame/WarGame.CPZ,BMF_JUST_MIRROR"$
#define BMF_JUST_MIRROR                0x400

#define BMF_LOCATE_NEAREST        0x800
#define BMF_DONT_DRAW                0x1000
#define BMF_ALIAS                0x2000
#define BMF_SCREEN_BITMAP        0x4000
#define BMF_FILL_NOT_COLOR        0x8000
#define BMF_RECORD_EXTENTS        0x10000
#define BMF_ON_TOP                0x20000

#define BMT_COLOR4        1
#define BMT_MONO        2

//This is using a byte plane instead of 4 bit planes.
//It doesn't help performance, however,
//might provide a starting point if anyone
//wants to do 24 bit color.
#define BMT_COLOR4_U1        3

#define BMS_SIGNATURE        0x28768922

public class GrSymStruct
{
 I4 sx,sy,sz,pad2;
 //Normal of symmetry plane
 I8 snx,sny,snz;
};

public class GrBitMap
{
 U0 start_saved_area;
 I4 type,width,width_internal,height;
 U8 flags;
 I8 plane_size;
 I4 left_margin,right_margin,top_margin,bottom_margin;
 U0 end_saved_area;

//public
 I4 color,bkcolor;
 I4 color2,pen_width;
 GrBitMap *brush;

 I8 *r;  //rotation matrix of quads decimal in lo
 I4 x,y,z,pad1;   //translation

 GrSymStruct sym;

//not document num, but num in a GrElems collection
 I8 nearest_grelem_num;

 U8 nearest_dist;

//not document num, but num in a GrElems collection
 I8 cur_grelem_num;

 I4 cur_x,cur_y,cur_z,pad3;
 double speedline_scale;
 U8 collision_cnt;

 //Set by $LK,"BMF_RECORD_EXTENTS","MN:BMF_RECORD_EXTENTS"$
 I8 min_x,max_x,min_y,max_y; //screen coordinates

 U4 bitmap_signature,pad4;
 TssStruct *mem_tss,*win_tss;
 GrBitMap *mask;
 U1 *body;
};


#help_index "Graphics/Bitmaps"

public void GrReset(GrBitMap *base)
{
 base->color=BLACK;
 base->color2=BLACK;
 base->bkcolor=BLACK;
 base->pen_width=1;
 base->flags&=~(BMF_SYMMETRY|BMF_TRANSFORMATION|BMF_JUST_MIRROR);
}

public GrBitMap *GrAlias(GrBitMap *base,TssStruct *tss)
{
 GrBitMap *result;
 if (!base) return NULL;
 result=MAlloc(sizeof(GrBitMap),tss);
 if (base->bitmap_signature!=BMS_SIGNATURE)
     Debugger;
 MemCpy(result,base,sizeof(GrBitMap));
 result->win_tss=tss;
 result->mem_tss=tss;
 result->r=MAlloc(16<<3,tss);
 GrSetIdent(result->r);
 GrReset(result);
 result->flags|=BMF_ALIAS;
 if (base->mask) {
     result->mask=GrAlias(base->mask,tss);
     result->mask->color=base->mask->color;
 }
 return result;
}


public GrBitMap *GrNew(U8 type,U8 width,U8 height,TssStruct *tss=NULL,BoolI1 null_bitmap=FALSE)
{  //This internally only allows widths which are divisible by 8
//$FG,4$Don't forget these$FG$
$MA+A-X+PU,"sizeof(GrBitMap)","Grep(\"sizeof(GrBitMap)\",\"/LT/\"TEXT_FILE_MASK);View;"$.
 GrBitMap *result;
 if (!tss) tss=Fs;
 result=MAllocZ(sizeof(GrBitMap),tss);
 result->win_tss=tss;
 result->mem_tss=tss;
 result->type=type;
 result->width=width;
 result->width_internal=(width+7)&~7;
 result->height=height;
 result->left_margin=0;
 result->right_margin=width;
 result->top_margin=0;
 result->bottom_margin=height;
 switch (type) {
     case BMT_COLOR4:
         result->plane_size=(result->width_internal*result->height)>>3;
         if (!null_bitmap)
             result->body=MAllocZ(result->plane_size<<2,tss);
         break;
     case BMT_COLOR4_U1:
         result->plane_size=result->width_internal*result->height;
         if (!null_bitmap)
             result->body=MAllocZ(result->plane_size,tss);
         break;
     case BMT_MONO:
         result->plane_size=(result->width_internal*result->height)>>3;
         if (!null_bitmap)
             result->body=MAllocZ(result->plane_size,tss);
         break;
     default:
         throw(EXCEPT_GRAPHICS,1);
 }
 if (null_bitmap)
     result->flags|=BMF_DONT_DRAW;
 result->pen_width=1;
 result->r=MAllocZ(16<<3,tss);
 result->r[0].u4[1]=1;
 result->r[5].u4[1]=1;
 result->r[10].u4[1]=1;
 result->r[15].u4[1]=1;
 result->speedline_scale=0.04;
 result->mask=NULL;
 result->bitmap_signature=BMS_SIGNATURE;
 return result;
}

public void GrDel(GrBitMap *base)
{
 if (!base) return;
 if (base->bitmap_signature!=BMS_SIGNATURE)
     Debugger;
 base->bitmap_signature=0;
 Free(base->r);
 if (!(base->flags & BMF_ALIAS))
     Free(base->body);
 GrDel(base->mask);
 Free(base);
}

public GrBitMap *GrCopy(GrBitMap *base,TssStruct *tss=NULL)
{
 GrBitMap *result;
 if (!base) return NULL;
 if (base->bitmap_signature!=BMS_SIGNATURE)
     Debugger;
 result=MAllocIdentical(base,tss);
 result->r=MAllocIdentical(base->r,tss);
 result->mem_tss=tss;
 result->body=MAllocIdentical(base->body,tss);
 result->mask=GrCopy(base->mask,tss);
 return result;
}

/* $AN,"GrBitMaps","GrBitMaps"$
$FG,2$        The format of a stored bitmap includes a
0x30 byte header.  See $LK,"GrBitMap","MN:GrBitMap"$.  The body
consists of bit planes like VGA except bits
in bytes are reversed.  For $LK,"BMT_MONO","MN:BMT_MONO"$, there
is one bit-plane; for $LK,"BMT_COLOR4","MN:BMT_COLOR4"$, there are
four bit-planes.

If you look at the color values ($LK,"BLACK","MN:BLACK"$-$LK,"WHITE","MN:WHITE"$)
you'll see the color of the planes is as follows:

Plane 0: BLUE
Plane 1: GREEN
Plane 2: RED
Plane 3: INTENSITY

If you are unfamiliar with bit planes, all
the blue bits for all the pixels are stored,
for all the green bits for all pixels, etc.  Therefore,
a single pixel's value is spread-out in mem
instead of residing in one location.  This
was done because that is how VGA is stored.
However, I made the order of the bits suitable
for doing $LK,"Bts","MN:Bts"$() or $LK,"Btr","MN:Btr"$() operations instead of
how VGA stores them.


The data for the following bitmap is displayed

width         : 11 pixels
height        :  8 pixels
internal width: 16 pixels (rounded-up to multiple of 8)
plane size    : 16 bytes

$PI,"",1$

#define RED                4
#define YELLOW                14


00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000010 00 00 00 00 00 00 F0 07 10 00 10 00 10 00 10 00
00000020 01 00 01 00 01 00 F1 07 11 00 11 00 11 00 11 00
00000030 00 00 00 00 00 00 F0 07 10 00 10 00 10 00 10 00

$FG$*/
public U1 *GrSave(GrBitMap *b,U8 *size=NULL,BoolI1 compressed=TRUE)
{ //stores to mem
 U1 *ptr,*body,*result;
 BoolI1 old_preempt;
 ArcCompressStruct *ac;
 I8 i,j,old_flags=b->flags;

 switch (b->type) {
     case BMT_COLOR4:
         i=b->plane_size<<2;
         break;
     case BMT_COLOR4_U1:
     case BMT_MONO:
         i=b->plane_size;
         break;
     default:
         throw(EXCEPT_GRAPHICS,2);
 }
 if (compressed) {
     ac=CompressBuf(b->body,i);
     j=ac->compressed_size;
     body=ac;

 } else {
     ac=NULL;
     j=i;
     body=b->body;
 }

 ptr=result=MAlloc(offset(GrBitMap.end_saved_area)-
         offset(GrBitMap.start_saved_area)+j);

 old_preempt=Preempt(OFF); //in case it is a screen bitmap
 if (compressed) b->flags|=BMF_COMPRESSED;
 b->flags&=BMF_COMPRESSED;
 MemCpy(result,&b->start_saved_area,
         offset(GrBitMap.end_saved_area)-
         offset(GrBitMap.start_saved_area));
 b->flags=old_flags;
 Preempt(old_preempt);

 ptr+=offset(GrBitMap.end_saved_area)-
         offset(GrBitMap.start_saved_area);
 MemCpy(ptr,body,j);
 ptr+=j;

 Free(ac);
 if (size)
     *size=ptr-result;
 return result;
}

public GrBitMap *GrLoad(U1 *src,U8 *size=NULL,TssStruct *tss=NULL)
{ //loads from mem
 I8 i;
 GrBitMap *result;
 U1 *ptr=src;
 ArcCompressStruct *ac;
 if (!tss) tss=Fs;
 result=MAllocZ(sizeof(GrBitMap),tss);
 result->win_tss=tss;
 result->mem_tss=tss;
 MemCpy(&result->start_saved_area,src,
         offset(GrBitMap.end_saved_area)-
         offset(GrBitMap.start_saved_area));
 ptr+=offset(GrBitMap.end_saved_area)-
         offset(GrBitMap.start_saved_area);
 switch (result->type) {
     case BMT_COLOR4:
         i=result->plane_size<<2;
         break;
     case BMT_COLOR4_U1:
     case BMT_MONO:
         i=result->plane_size;
         break;
     default:
         throw(EXCEPT_GRAPHICS,3);
 }
 if (result->flags&BMF_COMPRESSED) {
     result->flags&=~BMF_COMPRESSED;
     ac=ptr;
     result->body=ExpandBuf(ac,tss);
     ptr+=ac->compressed_size;
 } else {
     result->body=MAlloc(i,tss);
     MemCpy(result->body,ptr,i);
 }

 result->pen_width=1;
 result->r=MAllocZ(16<<3,tss);
 result->r[0].u4[1]=1;
 result->r[5].u4[1]=1;
 result->r[10].u4[1]=1;
 result->r[15].u4[1]=1;
 result->speedline_scale=0.04;
 result->bitmap_signature=BMS_SIGNATURE;
 result->mask=NULL;
 if (size)
     *size=ptr-src;
 return result;
}

public U8 WriteLGR(I1 *filename,GrBitMap *base)
{ // LoseThos LGR File
 U8 size;
 U1 *src=GrSave(base,&size);
 WriteFile(filename,src,size);
 Free(src);
 return size;
}

public GrBitMap *ReadLGR(I1 *filename)
{ // LoseThos LGR File
 GrBitMap *base=NULL;
 U1 *src=ReadFile(filename);
 if (src)
     base=GrLoad(src);
 Free(src);
 return base;
}

#help_index "Graphics/Bitmaps;Graphics/Screen"

public GrBitMap *GrCaptureScreen(TssStruct *tss=NULL)
{
 WinSync(TRUE);
 return GrCopy(gr_screen_image,tss);
}

public U8 CaptureScreenLGR(I1 *filename)
{ // LoseThos LGR File
 U8 size;
 GrBitMap *base=GrCaptureScreen;
 size=WriteLGR(filename,base);
 GrDel(base);
 return size;
}

#help_index "Graphics;Graphics/Bitmaps"

public void GrInitExtents(GrBitMap *base)
{//See $LK,"::/LT/Demo/Graphics/Extents.CPZ","FI:::/LT/Demo/Graphics/Extents.CPZ"$
//You should clear the record flag yourself
 base->flags|=BMF_RECORD_EXTENTS;
 base->min_x=MAX_I8;
 base->max_x=MIN_I8;
 base->min_y=MAX_I8;
 base->max_y=MIN_I8;
}

#help_index "Graphics"

public void GrPlot0(GrBitMap *base,I8 x,I8 y)
{  //No clipping or transformation or pen width
 U8 d,c,c2;
 U1 *dst,bit,not_bit;
 U8 color=base->color,bkcolor=base->bkcolor;
 double dist;

 if (base->flags & BMF_LOCATE_NEAREST) {
     dist=DistI8(x,y,base->cur_x,base->cur_y);
     if (dist<=base->nearest_dist) {
         base->nearest_grelem_num=base->cur_grelem_num;
         base->nearest_dist=dist;
     }
 }
 if (base->flags & BMF_RECORD_EXTENTS) {
     if (x<base->min_x) base->min_x=x;
     if (x>base->max_x) base->max_x=x;
     if (y<base->min_y) base->min_y=y;
     if (y>base->max_y) base->max_y=y;
 }
 if (base->flags & BMF_DONT_DRAW)
     return;
 d=base->width_internal*(y+base->top_margin)+x+base->left_margin;
 switch (base->type) {
     case BMT_COLOR4:
         dst=base->body+d>>3;
         d=base->plane_size;
         bit=1<<(x&7);
         not_bit=~bit;
         c=color.u1[0];
         switch (color.u1[3]) {
             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!=bkcolor.u1[0])
                     base->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;
             case ROPB_OR:
                 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;
             case ROPB_CLEAR_BITS:
                 if (c & 1) *dst&=not_bit;
                 dst+=d;
                 if (c & 2) *dst&=not_bit;
                 dst+=d;
                 if (c & 4) *dst&=not_bit;
                 dst+=d;
                 if (c & 8) *dst&=not_bit;
                 break;
         }
         break;
     case BMT_COLOR4_U1:
         dst=base->body+d;
         c=color.u1[0]&15;
         switch (color.u1[3]) {
             case ROPB_EQU:
             case ROPB_CLEAR_MASK_EQU:
             case ROPB_CLEAR_MASK_TRANSPARENT:
             case ROPB_TRANSPARENT:
                 *dst=c;
                 break;
             case ROPB_COLLISION:
                 if (*dst!=bkcolor.u1[0])
                     base->collision_cnt++;
                 break;
             case ROPB_XOR:
                 *dst^=c;
                 break;
             case ROPB_OR:
                 *dst|=c;
                 break;
             case ROPB_CLEAR_BITS:
                 *dst&=c^15;
                 break;
         }
         break;
     case BMT_MONO:
         c=color&0xFFFFFF;
         switch (color.u1[3]) {
             case ROPB_EQU:
             case ROPB_CLEAR_MASK_EQU:
             case ROPB_CLEAR_MASK_TRANSPARENT:
             case ROPB_TRANSPARENT:
                 AssignBit(base->body,d,c);
                 break;
             case ROPB_COLLISION:
                 if (c) {
                     if (bkcolor&0xFFFFFF)
                         base->collision_cnt+=Bt(base->body,d);
                     else
                         base->collision_cnt+=!Bt(base->body,d);
                 } else {
                     if (bkcolor&0xFFFFFF)
                         base->collision_cnt+=!Bt(base->body,d);
                     else
                         base->collision_cnt+=Bt(base->body,d);
                 }
                 break;
             case ROPB_XOR:
                 if (c)
                     Btc(base->body,d);
                 break;
             case ROPB_OR:
                 if (c)
                     Bts(base->body,d);
                 break;
             case ROPB_CLEAR_BITS:
                 if (c)
                     Btr(base->body,d);
                 break;
         }
         break;
 }
 if (base->mask) {
     base->mask->brush=base->brush;
     base->mask->color=WHITE;
     if (base->color.u1[3]==ROPB_CLEAR_MASK_EQU)
         base->mask->color=ROPB_EQU+BLACK;
     else if (base->color.u1[3]==ROPB_CLEAR_MASK_TRANSPARENT)
         base->mask->color=ROPB_TRANSPARENT+BLACK;
     else
         base->mask->color.u1[3]=base->color.u1[3];
     GrPlot0(base->mask,x,y);
 }
}

public I8 GrPeek0(GrBitMap *base,I8 x,I8 y)
{  //No clipping or transformation
 U8 d,c;
 U1 *src,bit;

 d=base->width_internal*(y+base->top_margin)+
     x+base->left_margin;
 switch (base->type) {
     case BMT_COLOR4:
         if (base->mask && !Bt(base->mask->body,d))
                 return MAX_I4;
         src=base->body+d>>3;
         bit=1<<(x&7);
         d=base->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 BMT_COLOR4_U1:
         if (base->mask && !Bt(base->mask->body,d))
                 return MAX_I4;
         src=base->body+d;
         return *src;
     case BMT_MONO:
         if (base->mask && !Bt(base->mask->body,d))
                 return MAX_I4;
         return Bt(base->body,d);
 }
}

#help_index "Graphics;Graphics/Bitmaps"

public BoolI8 GrBlot(GrBitMap *base,I8 my_x,I8 my_y,GrBitMap *img)
{  //Clipping but not transformation
 I8 x=my_x,y=my_y,reg i,i1,i2,i3,j,k,k1,kk,kk1,w1,h1,w2,h2,plane,plane1,reg bit_mask,reg
bit_shift,p,p1,dist;
 U1 reg *ptr;
 U2 reg *ptr1;
 I8 color,color2,reg color_byte,c,plane_size,plane_size1,plane_limit,plane_limit1;

 w1=x<0 ?-x:0;
 h1=y<0 ?-y:0;
 w2=img->width;
 h2=img->height;
 if (base->flags & BMF_SCREEN_BITMAP) {
     x+=base->win_tss->win_pixel_left;
     y+=base->win_tss->win_pixel_top;
 }
 if (base->flags & BMF_LOCATE_NEAREST) { //TODO:Untested
     dist=DistI8(x+img->width>>1,y+img->height>>1,base->cur_x,base->cur_y);
     if (dist<=base->nearest_dist) {
         base->nearest_grelem_num=base->cur_grelem_num;
         base->nearest_dist=dist;
     }
 }
 if (base->flags & BMF_SCREEN_BITMAP) {
     if (x+w1<0) w1=-x;
     if (x+w2>base->win_tss->win_pixel_right+1)
         w2=base->win_tss->win_pixel_right+1-x;

     if (y+h1<0) h1=-y;
     if (y+h2>base->win_tss->win_pixel_bottom+1)
         h2=base->win_tss->win_pixel_bottom+1-y;
 }
 x+=base->left_margin;
 y+=base->top_margin;
 if (x+w2>base->right_margin)
     w2=base->right_margin-x;
 if (y+h2>base->bottom_margin)
     h2=base->bottom_margin-y;
 if (w1<w2<=img->width && h1<h2<=img->height &&
         (!(base->flags & BMF_SCREEN_BITMAP) ||
         base->flags&BMF_ON_TOP ||
         (!IsPixelCovered(base->win_tss,x+w1-base->left_margin,y+h1-base->top_margin) &&
         !IsPixelCovered(base->win_tss,x+w2-base->left_margin,y+h2-base->top_margin)))) {
     if (base->flags & BMF_RECORD_EXTENTS) {
         if (x+w1<base->min_x) base->min_x=x+w1;
         if (x+w2-1>base->max_x) base->max_x=x+w2-1;
         if (y+h1<base->min_y) base->min_y=y+h1;
         if (y+h2-1>base->max_y) base->max_y=y+h2-1;
     }
     if (base->flags & BMF_DONT_DRAW)
         return TRUE;
     color=base->color;
     i1=(-w1)&7;
     if (i1>w2) i1=w2;
     i2=w2-w1-i1;
     if (i2<0)
         i2=0;
     else
         i2>>=3;
     i3=w2-w1-i2<<3-i1;
     bit_shift=x&7;
     bit_mask=-1-255<<bit_shift;
     switch (color.u1[3]) {
         case ROPB_COLLISION:
             plane_size=img->plane_size<<3;
             plane_size1=base->plane_size<<3;
             switch (base->type) {
                 case BMT_COLOR4:
                     plane_limit=plane_size*4;
                     plane_limit1=plane_size1*4;
                     break;
                 case BMT_MONO:
                     plane_limit=plane_size;
                     plane_limit1=plane_size1;
                     break;
             }
             color =base->bkcolor&0xFFFFFF;
             color2=img->bkcolor &0xFFFFFF;
             switch (img->type) {
                 case BMT_COLOR4:
                     k=h1*img->width_internal;
                     k1=(h1+y)*base->width_internal+x;
                     for (j=h2-h1;j;j--) {
                         for (i=w1;i<w2;i++) {
                             if (base->type==BMT_COLOR4_U1)
                                 c=base->body><(U1 *)[k1+i];
                             else {
                                 c=0;
                                 for (plane1=0,p=1;plane1<plane_limit1;
                                 plane1+=plane_size1,p<<=1)
                                     if (Bt(base->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)
                                     base->collision_cnt++;
                             }
                         }
                         k+=img->width_internal;
                         k1+=base->width_internal;
                     }
                     break;
                 case BMT_COLOR4_U1:
                     k=h1*img->width_internal;
                     k1=(h1+y)*base->width_internal+x;
                     for (j=h2-h1;j;j--) {
                         for (i=w1;i<w2;i++) {
                             if (base->type==BMT_COLOR4_U1)
                                 c=base->body><(U1 *)[k1+i];
                             else {
                                 c=0;
                                 for (plane1=0,p=1;plane1<plane_limit1;
                                 plane1+=plane_size1,p<<=1)
                                     if (Bt(base->body,k1+i+plane1))
                                         c+=p;
                                 }
                             if (c!=color) {
                                 if (img->body><(U1 *)[k+i]!=color2)
                                     base->collision_cnt++;
                             }
                         }
                         k+=img->width_internal;
                         k1+=base->width_internal;
                     }
                     break;
                 case BMT_MONO:
                     k=h1*img->width_internal;
                     k1=(h1+y)*base->width_internal+x;
                     for (j=h2-h1;j;j--) {
                         for (i=w1;i<w2;i++) {
                             if (base->type==BMT_COLOR4_U1)
                                 c=base->body><(U1 *)[k1+i];
                             else {
                                 c=0;
                                 for (plane1=0,p=1;plane1<plane_limit1;
                                 plane1+=plane_size1,p<<=1)
                                     if (Bt(base->body,k1+i+plane1))
                                         c+=p;
                                 }
                             if (c!=color) {
                                 if (Bt(img->body,k+i)!=color2)
                                     base->collision_cnt++;
                             }
                         }
                         k+=img->width_internal;
                         k1+=base->width_internal;
                     }
                     break;
             }
             break;
         case ROPB_TRANSPARENT:
         case ROPB_CLEAR_MASK_TRANSPARENT:
             switch (img->type) {
                 case BMT_COLOR4:
                     plane_size=img->plane_size<<3;
                     plane_limit=plane_size*4;
                     color2=img->bkcolor &0xFFFFFF;
                     k=h1*img->width_internal;
                     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) {
                                 base->color=c;
                                 GrPlot0(base,x+i-base->left_margin,y+j-base->top_margin);
                             }
                         }
                         k+=img->width_internal;
                     }
                     base->color=color;
                     break;
                 case BMT_COLOR4_U1:
                     color2=img->bkcolor &0xFFFFFF;
                     k=h1*img->width_internal;
                     for (j=h1;j<h2;j++) {
                         for (i=w1;i<w2;i++) {
                             c=img->body><(U1 *)[k+i];
                             if (c!=color2) {
                                 base->color=c;
                                 GrPlot0(base,x+i-base->left_margin,y+j-base->top_margin);
                             }
                         }
                         k+=img->width_internal;
                     }
                     base->color=color;
                     break;
                 default:
                     goto here1;
             }
             break;
         default:
             here1:
         plane_size=img->plane_size<<3;
             plane_size1=base->plane_size<<3;
             switch (base->type) {
                 case BMT_COLOR4:
                     plane_limit=plane_size*4;
                     plane_limit1=plane_size1*4;
                     break;
                 case BMT_COLOR4_U1:
                     if (img->type==BMT_COLOR4)
                         plane_limit=plane_size*4;
                     else
                         plane_limit=plane_size;
                     break;
                 case BMT_MONO:
                     plane_limit=plane_size;
                     plane_limit1=plane_size1;
                     break;
             }
             if (base->type==BMT_COLOR4 ||base->type==BMT_COLOR4_U1 ||img->type==BMT_MONO) {
                 if (base->type==BMT_COLOR4_U1) {
                     switch (img->type) {
                         case BMT_COLOR4:
                             switch (color.u1[3]) {
                                 case ROPB_EQU:
                                 case ROPB_CLEAR_MASK_EQU:
                                     for (p1=0,p=1,plane=0;plane<plane_limit;
                                     plane+=plane_size,p<<=1,p1++) {
                                         kk=h1*img->width_internal+w1;
                                         kk1=(h1+y)*base->width_internal+x+w1;
                                         for (j=h2-h1;j;j--) {
                                             k=kk+plane;
                                             ptr=img->body;
                                             ptr1=base->body+kk1;
                                             for (i=w2-w1;i;i--)
                                                 AssignBit(ptr1><(U1 *)++,p1,Bt(ptr,k++));
                                             kk+=img->width_internal;
                                             kk1+=base->width_internal;
                                         }
                                         }
                                     break;
                                 case ROPB_XOR:
                                     for (p1=0,p=1,plane=0;plane<plane_limit;
                                     plane+=plane_size,p<<=1,p1++) {
                                         kk=h1*img->width_internal+w1;
                                         kk1=(h1+y)*base->width_internal+x+w1;
                                         for (j=h2-h1;j;j--) {
                                             k=kk+plane;
                                             ptr=img->body;
                                             ptr1=base->body+kk1;
                                             for (i=w2-w1;i;i--,ptr1><(U1 *)++)
                                                 if (Bt(ptr,k++))
                                                     Btc(ptr1,p1);
                                             kk+=img->width_internal;
                                             kk1+=base->width_internal;
                                         }
                                         }
                                     break;
                                 case ROPB_OR:
                                     for (p1=0,p=1,plane=0;plane<plane_limit;
                                     plane+=plane_size,p<<=1,p1++) {
                                         kk=h1*img->width_internal+w1;
                                         kk1=(h1+y)*base->width_internal+x+w1;
                                         for (j=h2-h1;j;j--) {
                                             k=kk+plane;
                                             ptr=img->body;
                                             ptr1=base->body+kk1;
                                             for (i=w2-w1;i;i--,ptr1><(U1 *)++)
                                                 if (Bt(ptr,k++))
                                                     Bts(ptr1,p1);
                                             kk+=img->width_internal;
                                             kk1+=base->width_internal;
                                         }
                                         }
                                     break;
                                 case ROPB_CLEAR_BITS:
                                     for (p1=0,p=1,plane=0;plane<plane_limit;
                                     plane+=plane_size,p<<=1,p1++) {
                                         kk=h1*img->width_internal+w1;
                                         kk1=(h1+y)*base->width_internal+x+w1;
                                         for (j=h2-h1;j;j--) {
                                             k=kk+plane;
                                             ptr=img->body;
                                             ptr1=base->body+kk1;
                                             for (i=w2-w1;i;i--,ptr1><(U1 *)++)
                                                 if (Bt(ptr,k++))
                                                     Btr(ptr1,p1);
                                             kk+=img->width_internal;
                                             kk1+=base->width_internal;
                                         }
                                         }
                                     break;
                             }
                             break;
                         case BMT_COLOR4_U1:
                             for (p1=0,p=1,plane=0;plane<plane_limit;
                             plane+=plane_size,p<<=1,p1++) {
                                 kk=h1*img->width_internal+w1;
                                 kk1=(h1+y)*base->width_internal+x+w1;
                                 switch (color.u1[3]) {
                                     case ROPB_EQU:
                                     case ROPB_CLEAR_MASK_EQU:
                                         for (j=h2-h1;j;j--) {
                                             ptr=img->body+kk;
                                             ptr1=base->body+kk1;
                                             for (i=w2-w1;i;i--)
                                                 *ptr1><(U1 *)++=*ptr++;
                                             kk+=img->width_internal;
                                             kk1+=base->width_internal;
                                         }
                                         break;
                                     case ROPB_XOR:
                                         for (j=h2-h1;j;j--) {
                                             ptr=img->body+kk;
                                             ptr1=base->body+kk1;
                                             for (i=w2-w1;i;i--)
                                                 *ptr1><(U1 *)++^=*ptr++;
                                             kk+=img->width_internal;
                                             kk1+=base->width_internal;
                                         }
                                         break;
                                     case ROPB_OR:
                                         for (j=h2-h1;j;j--) {
                                             ptr=img->body+kk;
                                             ptr1=base->body+kk1;
                                             for (i=w2-w1;i;i--)
                                                 *ptr1><(U1 *)++|=*ptr++;
                                             kk+=img->width_internal;
                                             kk1+=base->width_internal;
                                         }
                                         break;
                                     case ROPB_CLEAR_BITS:
                                         for (j=h2-h1;j;j--) {
                                             ptr=img->body+kk;
                                             ptr1=base->body+kk1;
                                             for (i=w2-w1;i;i--)
                                                 *ptr1><(U1 *)++&=~*ptr++;
                                             kk+=img->width_internal;
                                             kk1+=base->width_internal;
                                         }
                                         break;
                                 }
                             }
                             break;
                         case BMT_MONO:
                             for (p1=0,p=1,plane=0;plane<plane_limit;
                             plane+=plane_size,p<<=1,p1++) {
                                 kk=h1*img->width_internal+w1;
                                 kk1=(h1+y)*base->width_internal+x+w1;
                                 switch (color.u1[3]) {
                                     case ROPB_EQU:
                                     case ROPB_CLEAR_MASK_EQU:
                                         for (j=h2-h1;j;j--) {
                                             k=kk;
                                             ptr=img->body;
                                             ptr1=base->body+kk1;
                                             for (i=w2-w1;i;i--)
                                                 if (Bt(ptr,k++))
                                                     *ptr1><(U1 *)++=color.u1[0];
                                                 else
                                                     *ptr1><(U1 *)++=0;
                                             kk+=img->width_internal;
                                             kk1+=base->width_internal;
                                         }
                                         break;
                                     case ROPB_CLEAR_MASK_TRANSPARENT:
                                     case ROPB_TRANSPARENT:
                                         for (j=h2-h1;j;j--) {
                                             k=kk;
                                             ptr=img->body;
                                             ptr1=base->body+kk1;
                                             for (i=w2-w1;i;i--,ptr1><(U1 *)++)
                                                 if (Bt(ptr,k++))
                                                     *ptr1><(U1 *)=color.u1[0];
                                             kk+=img->width_internal;
                                             kk1+=base->width_internal;
                                         }
                                         break;
                                     case ROPB_XOR:
                                         for (j=h2-h1;j;j--) {
                                             k=kk;
                                             ptr=img->body;
                                             ptr1=base->body+kk1;
                                             for (i=w2-w1;i;i--,ptr1><(U1 *)++)
                                                 if (Bt(ptr,k++))
                                                     *ptr1><(U1 *)^=color.u1[0];
                                             kk+=img->width_internal;
                                             kk1+=base->width_internal;
                                         }
                                         break;
                                     case ROPB_OR:
                                         for (j=h2-h1;j;j--) {
                                             k=kk;
                                             ptr=img->body;
                                             ptr1=base->body+kk1;
                                             for (i=w2-w1;i;i--,ptr1><(U1 *)++)
                                                 if (Bt(ptr,k++))
                                                     *ptr1><(U1 *)|=color.u1[0];
                                             kk+=img->width_internal;
                                             kk1+=base->width_internal;
                                         }
                                         break;
                                     case ROPB_CLEAR_BITS:
                                         for (j=h2-h1;j;j--) {
                                             k=kk;
                                             ptr=img->body;
                                             ptr1=base->body+kk1;
                                             for (i=w2-w1;i;i--,ptr1><(U1 *)++)
                                                 if (Bt(ptr,k++))
                                                     *ptr1><(U1 *)&=~color.u1[0];
                                             kk+=img->width_internal;
                                             kk1+=base->width_internal;
                                         }
                                         break;
                                 }
                                 break;
                             }
                             break;
                     }
                 } else {
                     for (p1=0,p=1,plane=0,plane1=0;plane<plane_limit;
                     plane+=plane_size,plane1+=plane_size1,p<<=1,p1++) {
                         if (Bt(&color,p1))
                             color_byte=255;
                         else
                             color_byte=0;
                         kk=h1*img->width_internal+w1;
                         kk1=(h1+y)*base->width_internal+x+w1;
                         switch (img->type) {
                             case BMT_COLOR4:
                                 switch (color.u1[3]) {
                                     case ROPB_EQU:
                                     case ROPB_CLEAR_MASK_EQU:
                                         for (j=h2-h1;j;j--) {
                                             k=kk+plane;
                                             k1=kk1+plane1;
                                             ptr=img->body;
                                             ptr1=base->body;
                                             for (i=0;i<i1;i++,k1++)
                                                 AssignBit(ptr1,k1,Bt(ptr,k++));
                                             ptr+=k>>3;
                                             ptr1><(U1 *)+=k1>>3;
                                             for (i=0;i<i2;i++,ptr1><(U1 *)++)
                    &n