#help_index "Graphics/Bitmaps;Graphics/Screen"
public GrBitMap *gr_screen_image; //This is read only

#help_index "Graphics/Bitmaps"
public GrBitMap *grbase; //This is updated every refresh
GrBitMap *gr_scaled_base;
U2 *gr_textbase_cache;

void GrClear2(GrBitMap *base)
{
 switch (base->type) {
     case BMT_COLOR4:
         MemSet(base->body,0,(base->width_internal*base->height)>>1);
         break;
     case BMT_COLOR4_U1:
         MemSet(base->body,0,base->width_internal*base->height);
         break;
     case BMT_MONO:
         MemSet(base->body,0,(base->width_internal*base->height)>>3);
         break;
 }
 if (base->mask)
     GrClear2(base->mask);
}

public void GrClear(GrBitMap *base=NULL)
{
 if (!base)
     GrClear2(Gs->grbase);
 else
     GrClear2(base);
}

#help_index "Graphics/Char;Char"

public void FillWinText(TssStruct *tss,U8 d)
{
 I8 x,y;
 if (!tss) tss=Fs;
 I8 t= tss->win_top>0
           ? tss->win_top : 0,
         b= tss->win_bottom<TEXT_ROWS
           ? tss->win_bottom : TEXT_ROWS-1,
         l= tss->win_left>0
           ? tss->win_left : 0,
         r= tss->win_right<TEXT_COLS
           ? tss->win_right : TEXT_COLS-1;
 U4 *ptr;
 for (y=t;y<=b;y++) {
     ptr=textbase><(U1 *)+(y*TEXT_COLS+l)*4;
     for (x=l;x<=r;x++)
         *ptr++=d;
 }
}

public void ClearWinText(TssStruct *tss=NULL)
{
 if (!tss) tss=Fs;
 FillWinText(tss,tss->text_attr<<8);
}

public void SetWinBkColor(TssStruct *tss,U8 c)
{
 if (!tss) tss=Fs;
 tss->text_attr=tss->text_attr&0x0F | c<<4;
 FillWinText(tss,c<<12);
}

#help_index "Graphics/Screen"
I4 gr_table[1024];

public I4 gr_rainbow_10[10]=
{
BLACK,BROWN,RED,LTRED,YELLOW,
GREEN,BLUE,PURPLE,LTGRAY,WHITE
};

LoadList("ST_RAINBOW_10",
"BLACK\0BROWN\0RED\0LTRED\0YELLOW\0"
"GREEN\0BLUE\0PURPLE\0LTGRAY\0WHITE\0");

#define GR_NUM_PEN_BRUSHES 64
GrBitMap *gr_pen_brushes[GR_NUM_PEN_BRUSHES];

#define GR_MAX_SCREEN_SCALE        8
U1 *gr_screen_scale_tables[GR_MAX_SCREEN_SCALE+1];
I8 gr_screen_scale=1,grsx=0,grsy=0;

//When zoomed, this keeps the mouse centered.
public BoolI1 gr_continuous_scroll=FALSE;


#define GR_REFRESH_LOG_CNT        16
U8 gr_refresh_time_stamps[GR_REFRESH_LOG_CNT],gr_refresh_ts_ptr=0;

void GrSetupTable(GrBitMap *hidden)
{
 GrBitMap *b;
 I8 i,j,k,l,m,x,y,rr;
 U1 *dst;
 for (i=-16;i<16;i++) {
     for (j=-16;j<16;j++) {
         k=(i>=0 ? i:32+i)<<5+(j>=0 ? j:32+j);
         gr_table[k]=(i*hidden->width_internal+j)>>3;
     }
 }
 for (i=0;i<GR_NUM_PEN_BRUSHES;i++) {
     k=i+1;
     j=(i+7)&~7;
     b=GrNew(BMT_MONO,i,i);
//    b=GrNew(BMT_MONO,j,i);
     gr_pen_brushes[i]=b;
     b->color=WHITE;
     rr=k*k;
     for (y=1;y<k;y++)
         for (x=1;x<k;x++)
             if (SqrI8(y*2-k)+SqrI8(x*2-k)<rr)
                 GrPlot0(b,x-1,y-1);
 }
 if (text_mode) {
     MemSet(0xB8000,0,TEXT_ROWS*TEXT_COLS*2);
     MemSet(gr_textbase_cache,0,TEXT_ROWS*TEXT_COLS*2);
 } else {
     OutP(VGA_SC_INDEX,VGA_MAP_MASK);
     OutP(VGA_SC_DATA,0x0F);
     MemSet(0xA0000,0,GR_HEIGHT*GR_WIDTH>>3);
     MemSet(gr_screen_image->body,0,GR_WIDTH*GR_HEIGHT>>1);
 }
 for (i=1;i<=GR_MAX_SCREEN_SCALE;i++) {
     dst=gr_screen_scale_tables[i]=MAlloc(256*i);
     for (j=0;j<256;j++) {
         m=0;
         for (k=0;k<8;k++) {
             if (Bt(&j,k)) {
                 for (l=0;l<i;l++)
                     Bts(&m,l+k*i);
             }
         }
         for (l=0;l<i;l++)
             dst[j+l*256]=m.u1[l];
     }
 }
 for (i=0;i<GR_REFRESH_LOG_CNT;i++)
     gr_refresh_time_stamps[i]=GetTimeStamp;
}

void GrInitCPU(CPUCachedStruct *c)
{ //Gets called from $LK,"GrInitCPU","FF:::/LT/OSMain/Cmd.CPZ,GrInitCPU"$
 c->grbase=GrNew(BMT_COLOR4,GR_WIDTH,GR_HEIGHT);
 c->grbase->flags|=BMF_SCREEN_BITMAP;
 c->grbase->mask=GrNew(BMT_MONO,GR_WIDTH,GR_HEIGHT);
 c->grbase->mask->flags|=BMF_SCREEN_BITMAP;
 c->grbase->mask->color=WHITE;
 GrClear2(c->grbase);
}

void GrInitGraphics()
{
 textbase=MAlloc(GR_WIDTH/FONT_WIDTH*(GR_HEIGHT/FONT_HEIGHT)<<2);
 gr_textbase_cache=MAlloc(TEXT_ROWS*TEXT_COLS*2);

 grbase=GrNew(BMT_COLOR4,GR_WIDTH+128,GR_HEIGHT+32);
 grbase->left_margin=64;
 grbase->right_margin=GR_WIDTH+grbase->left_margin;
 grbase->top_margin=16;
 grbase->bottom_margin=GR_HEIGHT+grbase->top_margin;
 grbase->flags|=BMF_SCREEN_BITMAP;

 gr_screen_image=GrNew(BMT_COLOR4,GR_WIDTH,GR_HEIGHT);
 gr_scaled_base=GrNew(BMT_COLOR4,GR_WIDTH,GR_HEIGHT);
 gr_scaled_base->flags|=BMF_SCREEN_BITMAP;
 GrInitCPU(Gs);
 GrSetupTable(grbase);
}

U8 gr_win_updates=0;

void GrUpdateWins()
{
 I8 i;
 TssStruct *tss,*tss1;
 GrBitMap *base;
 refresh_ode_time=0;
 try {
     for (i=0;i<mp_cnt;i++) {
         if (i)
             tss=cpu_cached[i].seth_tss;
         else
             tss=Fs;
         tss1=tss;
         do {
             if (!ValidateTss(tss))
                 break;
             sys_task_being_screen_updated=tss;
             UpdateOdes(tss);
             if (!ValidateTss(tss))
                 break;
             if (Bt(&tss->display_flags,DISPLAYf_SHOW)) {
                 if (!Bt(&tss->display_flags,DISPLAYf_NO_BORDER))
                     DrawTaskBorder(tss);
                 ClearWinText(tss);
                 if (tss->update_win)
                     tss->update_win(tss);
                 if (tss->draw_it) {
                     base=GrAlias(grbase,tss);
                     tss->draw_it(tss,base);
                     GrDel(base);
                 }
                 if (!ValidateTss(tss))
                     break;
                 DrawCtrls(tss);
             }
             if (!ValidateTss(tss))
                 break;
             tss=tss->next_tss;
         } while (tss!=tss1);
     }
 } catch
     Debugger;
 last_refresh_ode_time=refresh_ode_time;
 sys_task_being_screen_updated=NULL;
 gr_refresh_time_stamps[gr_refresh_ts_ptr++&(GR_REFRESH_LOG_CNT-1)]=GetTimeStamp;
 win_actual_refresh=(GR_REFRESH_LOG_CNT-1)*time_stamp_freq/
                                       
(gr_refresh_time_stamps[(gr_refresh_ts_ptr-1)&(GR_REFRESH_LOG_CNT-1)]-
                                         
gr_refresh_time_stamps[gr_refresh_ts_ptr&(GR_REFRESH_LOG_CNT-1)]);
 gr_win_updates++;
}


void GrMergePersistentLayers()
{
 U8 reg *mask_ptr,reg *m_dst,reg *m_src;
 U8 i,reg j,row,reg col,plane,
     d0=Gs->grbase->width_internal>>6,
     d1=(grbase->width_internal*grbase->top_margin)>>6;

 for (i=0;i<mp_cnt;i++) {
     m_src=cpu_cached[i].grbase->body;
     m_dst=grbase->body;
     for (plane=1;plane<0x10;plane<<=1) {
         mask_ptr=cpu_cached[i].grbase->mask->body;
         m_dst+=d1;
         for (row=0;row<Gs->grbase->height;row++) {
             m_dst++;
             for (col=0;col<d0;col++) {
                 if (j=*mask_ptr)
                     *m_dst=*m_dst&~j|*m_src&j;
                 m_dst++;
                 mask_ptr++;
                 m_src++;
             }
             m_dst++;
         }
         m_dst+=d1;
     }
 }
}


void GrFixScale()
{
 gr_screen_scale=LimitI8(gr_screen_scale,1,GR_MAX_SCREEN_SCALE);
 if (gr_screen_scale==1) {
     grsx=0;
     grsy=0;
 } else {
     grsx=LimitI8(grsx,0,GR_WIDTH-GR_WIDTH/gr_screen_scale)&~7;
     grsy=LimitI8(grsy,0,GR_HEIGHT-GR_HEIGHT/gr_screen_scale);
 }
}


public void GrScaleZoom(double scale)
{
 BoolI1 old_preempt=Preempt(OFF);
 double s=gr_screen_scale;
 gr_screen_scale=gr_screen_scale*scale;
 GrFixScale;
 s/=gr_screen_scale;
 ipx_scale*=s;
 ipy_scale*=s;
 ipz_scale*=s;
 ipx_offset=ipx-(ipx-ipx_offset)*s;
 ipy_offset=ipy-(ipy-ipy_offset)*s;
 ipz_offset=ipz-(ipz-ipz_offset)*s;
 grsx=ipx-gr_scaled_base->width >>1/gr_screen_scale;
 grsy=ipy-gr_scaled_base->height>>1/gr_screen_scale;
 GrFixScale;
 Preempt(old_preempt);
}

public void GrZoomIn()
{
 GrScaleZoom(2.0);
}

public void GrZoomOut()
{
 GrScaleZoom(0.5);
}

void CtrlAltZ(U8 sc)
{
 if (sc&SCF_SHIFT)
     GrZoomOut;
 else
     GrZoomIn;
}
SetCtrlAltLetterRoutine('Z',&CtrlAltZ,"Sys/Zoom In or Out");

void GrScaleImage()
{
 GrFixScale;
 I8 plane,row,col,k,l,
     d1=(grbase->width_internal*grbase->top_margin)>>3,
     d2=gr_scaled_base->width>>3/gr_screen_scale,
     d4=gr_scaled_base->width_internal>>3,
     d5=d4-d2*gr_screen_scale,
     d3=gr_scaled_base->height/gr_screen_scale,
     d6=(gr_scaled_base->height-d3)*grbase->width_internal>>3,
     d7=gr_scaled_base->height%gr_screen_scale*d4;
 U1 *src,*src2,*dst,*src3,*map=gr_screen_scale_tables[gr_screen_scale];

 src=grbase->body+grsx>>3+grsy*grbase->width_internal>>3;
 dst=gr_scaled_base->body;
 for (plane=1;plane<0x10;plane<<=1) {
     src+=d1;
     for (row=0;row<d3;row++) {
         src+=64>>3;
         for (k=1;k<=gr_screen_scale;k++) {
             src2=src;
             for (col=0;col<d2;col++) {
                 src3=&map[*src2++];
                 for (l=1;l<=gr_screen_scale;l++) {
                     *dst++=*src3;
                     src3+=256;
                 }
             }
             for (l=0;l<d5;l++)
                 *dst++=0;
         }
         src+=d4+64>>3;
     }
     for (l=0;l<d7;l++)
         *dst++=0;
     src+=d1+d6;
 }
}


void GrUpdateBackgroundOfText()
{
 U8  d1=grbase->width_internal>>3,
         d2=(grbase->width_internal*grbase->height-FONT_HEIGHT*grbase->width)>>3,
         d4=(grbase->width_internal*FONT_HEIGHT-Gs->grbase->width)>>3,
         d7=1-(grbase->width_internal*grbase->height<<2)>>3;
 U8 row,col;
 U4 *src,cur_ch;
 U1 *dst;
 BoolI1 blink_flag=Blink;

 src=textbase;
 dst=grbase->body+(grbase->width_internal*grbase->top_margin+grbase->left_margin)>>3;
 for (row=0;row<TEXT_ROWS;row++) {
     for (col=0;col<TEXT_COLS;col++) {
         cur_ch=*src++;
         if (cur_ch & (LTFLT_SELECTED | LTFLT_INVERT | LTFLT_BLINK)) {
             if (cur_ch & LTFLT_SELECTED)
                 cur_ch.u1[1]=cur_ch.u1[1]^0xFF;
             if (cur_ch & LTFLT_INVERT)
                 cur_ch.u1[1]=cur_ch.u1[1]<<4+cur_ch.u1[1]>>4;
             if (cur_ch & LTFLT_BLINK)
                 if (blink_flag)
                     cur_ch.u1[1]= cur_ch.u1[1]<<4+ cur_ch.u1[1]>>4;
         }
         GrPlotBackground(cur_ch.u1[1],&dst,d1,d2);
         dst+=d7;
     }
     dst+=d4;
 }
}

$AN,"ExtScanCodes","ExtScanCodes"$
//   Bits 0-7        ASCII (Screen Code)
//   Bits 8-11        Foreground color
//   Bits 12-15 Background color
//   Bits 16-20 Signed X position shift value
//   Bits 21-25 Signed Y position shift value
//   Bit  28        Blink
//   Bit  29        Inverted (Swap foreground and background)
//   Bit  30        Selected (XOR colors with FF)
//   Bit  31        Underline

void GrUpdateForegroundOfText()
{
 U4 *src;
 U8 u,cur_ch,row,col,plane,ch_line,i,
         d1=grbase->width_internal>>3,
         d2=(grbase->width_internal*grbase->height-FONT_HEIGHT*grbase->width)>>3,
         d4=(grbase->width_internal*FONT_HEIGHT-Gs->grbase->width)>>3;
 U2 *dst2;
 U1 *dst,*font_ptr,*font_ptr2,saved_font_line;
 BoolI1 blink_flag=Blink,skip_space;

 font_ptr=&grfont[FONT_WIDTH*FONT_HEIGHT>>3*CH_SPACE];
 skip_space=TRUE;
 for (row=0;row<FONT_HEIGHT;row++)
     if (font_ptr[row]) {
         skip_space=FALSE;
         break;
     }
 src=textbase;
 dst=grbase->body+(grbase->width_internal*grbase->top_margin+grbase->left_margin)>>3;
 for (row=0;row<TEXT_ROWS;row++) {
     for (col=0;col<TEXT_COLS;col++) {
         cur_ch=*src++;
         if (cur_ch & (LTFLT_UNDERLINE | LTFLT_SELECTED | LTFLT_INVERT | LTFLT_BLINK)) {
             if (cur_ch & LTFLT_SELECTED)
                 cur_ch.u1[1]=cur_ch.u1[1]^0xFF;
             if (cur_ch & LTFLT_INVERT)
                 cur_ch.u1[1]=cur_ch.u1[1]<<4+cur_ch.u1[1]>>4;
             if (cur_ch & LTFLT_BLINK)
                 if (blink_flag)
                     cur_ch.u1[1]= cur_ch.u1[1]<<4+cur_ch.u1[1]>>4;
         } else
             if (!cur_ch.u1[0] || skip_space && cur_ch.u1[0]==CH_SPACE)
                 goto skip_char;
         font_ptr=&grfont[FONT_WIDTH*FONT_HEIGHT>>3*cur_ch.u1[0]];
         if (cur_ch & LTFLT_UNDERLINE) {
             saved_font_line=font_ptr[7];
             font_ptr[7]=0xFF;
         }
         i=cur_ch.u2[1]&0x3FF;
         dst2=dst+gr_table[i];
         if (i) {
             i&=7;
             for (plane=0x01;plane!=0x10;plane<<=1) {
                 font_ptr2=font_ptr;
                 if (cur_ch.u1[1] & plane) {
                     for (ch_line=0;ch_line<FONT_HEIGHT;ch_line++) {
                         u=*font_ptr2++<<i;
                         *dst2|=u.u2[0] | u.u2[1];
                         dst2><(U1 *)+=d1;
                     }
                     } else {
                     for (ch_line=0;ch_line<FONT_HEIGHT;ch_line++) {
                         u=*font_ptr2++<<i;
                         *dst2&=~(u.u2[0] | u.u2[1]);
                         dst2><(U1 *)+=d1;
                     }
                     }
                 dst2><(U1 *)+=d2;
             }
         } else
             GrPlotChar(font_ptr,cur_ch.u1[1],&dst2,d1,d2);
         if (cur_ch & LTFLT_UNDERLINE)
             font_ptr[7]=saved_font_line;
skip_char:
         dst++;
     }
     dst+=d4;
 }
}

void GrUpdateTextModeText()
{
 U4 *src=textbase;
 U8 cur_ch,i,d0=TEXT_COLS*TEXT_ROWS;
 U2 *dst=0xB8000,*dst2=gr_textbase_cache;
 BoolI1 blink_flag=Blink;
 if (LBtr(&sys_semas[SYS_SEMA_FLUSH_VGA_IMAGE],0)) {
     for (i=0;i<d0;i++) {
         cur_ch=*src++;
         if (cur_ch & LTFLT_SELECTED)
             cur_ch.u1[1]=cur_ch.u1[1]^0xFF;
         if (cur_ch & LTFLT_INVERT)
             cur_ch.u1[1]=cur_ch.u1[1]<<4+cur_ch.u1[1]>>4;
         if (cur_ch & LTFLT_BLINK)
             if (blink_flag)
                 cur_ch.u1[1]= cur_ch.u1[1]<<4+ cur_ch.u1[1]>>4;
         cur_ch&=0x7FFF;
         *dst++=*dst2++=cur_ch;
     }
 } else {
     for (i=0;i<d0;i++) {
         cur_ch=*src++;
         if (cur_ch & LTFLT_SELECTED)
             cur_ch.u1[1]=cur_ch.u1[1]^0xFF;
         if (cur_ch & LTFLT_INVERT)
             cur_ch.u1[1]=cur_ch.u1[1]<<4+cur_ch.u1[1]>>4;
         if (cur_ch & LTFLT_BLINK)
             if (blink_flag)
                 cur_ch.u1[1]= cur_ch.u1[1]<<4+ cur_ch.u1[1]>>4;
         cur_ch&=0x7FFF;
         if (*dst2!=cur_ch)
             *dst++=*dst2++=cur_ch;
         else {
             dst++;
             dst2++;
         }
     }
 }
}

void GrUpdateVGAGraphics()
{
 U8 row,plane,d0,d1,
         d2=gr_scaled_base->width_internal>>6;
 U4 *src,*dst,*dst2;

 //Update Graphic Card
 if (gr_screen_scale==1) {
     src=grbase->body;
     d1=(grbase->width_internal*grbase->top_margin)>>5,
     d0=64>>5;
 } else {
     GrScaleImage;
     src=gr_scaled_base->body;
     d1=0;
     d0=0;
 }
 dst2=gr_screen_image->body;
 if (LBtr(&sys_semas[SYS_SEMA_FLUSH_VGA_IMAGE],0)) {
     for (plane=1;plane<0x10;plane<<=1) {
         OutP(VGA_SC_INDEX,VGA_MAP_MASK);
         OutP(VGA_SC_DATA,plane);
         dst=0xA0000;
         src+=d1;
         for (row=0;row<gr_scaled_base->height;row++) {
             src+=d0;
             GrUpdateLineU8FlushCache(&dst,&src,d2,&dst2);
             src+=d0;
         }
         src+=d1;
     }
     } else {
     for (plane=1;plane<0x10;plane<<=1) {
         OutP(VGA_SC_INDEX,VGA_MAP_MASK);
         OutP(VGA_SC_DATA,plane);
         dst=0xA0000;
         src+=d1;
         for (row=0;row<gr_scaled_base->height;row++) {
             src+=d0;
             GrUpdateLineU8(&dst,&src,d2,&dst2);
             src+=d0;
         }
         src+=d1;
     }
 }
}


void GrUpdateScreen()
{
 GrBitMap *base;
 BoolI1 old_preempt=Preempt(ON);

 if (text_mode)
     GrUpdateWins;
 else {
     GrUpdateBackgroundOfText;
     GrUpdateForegroundOfText;
     GrUpdateWins;
     GrMergePersistentLayers;
 }

 base=GrAlias(grbase,Fs);
 base->flags|=BMF_ON_TOP;
 CallExtNum(EXT_FINAL_SCREEN_UPDATE,base);
 GrDel(base);

 if (text_mode)
     GrUpdateTextModeText;
 else
     GrUpdateVGAGraphics;
 Preempt(old_preempt);
}

#help_index "Graphics"
Home

Kernel
StartUp
Scheduler
Memory
MultiCore
Interrupts
BitMaps
Screen
Plot
Compiler Frontend
Compiler Mid
Compiler Backend
Graphics to Screen