/* Betty-TV-Fernbedienung: Grafik-Routinen */

/* Prinzip: Am Ende des RAM's oberhalb des Stacks befinden
   sich 2 Bildspeicher zu je 2560 Bytes (je einer für
   bit 1 und bit 2 jedes Pixels), in die hineingeschrieben,
   gemalt und gezeichnet wird.
   Dabei werden die veränderten "pages", also die je 8 Pixel
   hohen Streifen wo sich was geändert hat markiert.

   Die Funktion ScrBlt() überträgt - wenn man sie aufruft -
   diese geänderten Streifen ins LCD.

   Fonts: ich habe erstmal 6 Fonts eingebaut,
   zwei (5x7 und 6x13) mit fester Breite, die anderen proportional.
*/

#include "StdTypes.h"
#include "LPC22xx.h"
#include "setup.h"
#include "gdi.h"

#include "Fonts\Simple5x7_7.c"
#include "Fonts\Simple6x13_13.c"
#include "Fonts\Helvetica_14.c"
#include "Fonts\Helvetica_Bold_14.c"
#include "Fonts\Helvetica_12.c"
#include "Fonts\Helvetica_Bold_12.c"
#include "Fonts\Lucida_Bold_20.c"

byte Kontrast;

PFONT const FontPool[FontAnzahl] =
{ (PFONT) Simple5x7_7,
  (PFONT) Simple6x13_13,
  (PFONT) Helvetica_12,
  (PFONT) Helvetica_Bold_12,
  (PFONT) Helvetica_14,
  (PFONT) Helvetica_Bold_14,
  (PFONT) Lucida_Bold_20
};


/**************************************************************/
/*  Low Level und Initialisierung des LCD                     */
/**************************************************************/
void pause(void)
{dword xx;
  xx = 1000;
  while(--xx);
}


/* den gesamten Bildspeicher ablöschen mit Farbe
   =============================================
 */
void FillLanes(byte mode)
{ byte b1, b2;
  int i;
  switch (mode)
  { case black: b1 = b2 = 0xFF;
                break;
    case dkgr:  b1 = 0; b2 = 0xFF;
                break;
    case ltgr:  b1 = 0xFF; b2 = 0;
                break;
    default:    b1 = b2 = 0;
  }
  i = sizeof(PixLane1);
  while (i)
  { --i;
    PixLane1[i] = b1;
    PixLane2[i] = b2;
  }
  DirtyPages = 0x000FFFFF;
}


/* den Bildspeicher zum LCD übertragen
   ===================================
 */
int ScrBlt (void)               // returns # transferred bytes
{ word page, i, idx;
  int cnt;

  if (!DirtyPages) return 0;

  cnt = page = idx = 0;
  while (page < 20)
  { if (DirtyPages AND (1<<page))
    { LCD_CMD = 0xB0;
      LCD_CMD = page;
      LCD_CMD = 0x10;
      LCD_CMD = 0;
      i = 0;
      while (i<128)
      { LCD_DATA = PixLane1[idx];
        LCD_DATA = PixLane2[idx];
	++idx;
	++i;
	++cnt;
      }
      DirtyPages = DirtyPages AND NOT(1<<page);
    }
    else idx = idx + 128;
    ++page;
  }
  return cnt;
}



/* die Orientierung des Bildschirms ändern
   =======================================
Die X-Orientierung greift nur zwischen CPU und Display-RAM,
was bedeutet, daß man nach X-Ändern (A0 <--> A1)   den
gesamten Bildschirm nochmal ins Display übertragen muß.
 */
void FlipDisplay (bool normal)
{ if (normal)
   { LCD_CMD = 0xC8;		/* in Y-Richtung normal */
     LCD_CMD = 0xA0;		/* in X-Richtung normal */
   }
  else
   { LCD_CMD = 0xC0;		/* in Y-Richtung invertiert */
     LCD_CMD = 0xA1;		/* in X-Richtung invertiert */
   }
  DirtyPages = 0x000FFFFF;      /* beim nächsten Mal alles  */
}


/* die Beleuchtung ein und ausschalten
   ===================================
 */

void LichtAus (void)
{ FIO0SET = (1<<4);} /* LCD Beleuchtung aus */

void LichtEin (void)
{ FIO0CLR = (1<<4);} /* LCD Beleuchtung ein */


/* das Display hardwareseitig initialisieren
   =========================================
 */
void init_GDI (void)
{ pause();
  LCD_CMD = 0x56;  /* 160 mux           */
  LCD_CMD = 0xAB;  /* start Oszillator  */
  LCD_CMD = 0x66;  /* DC-DC = 5x             */
  pause();
  LCD_CMD = 0x2F;  /* turn on: booster+regulator+buffer  */
  pause();
  LCD_CMD = 0x27;  /* regulator gain 8.1  */
  pause();
  LCD_CMD = 0x81;  /* set contrast  */
  LCD_CMD = 0x30;  /* auf 48  */
  pause();

  LCD_CMD = 0x93;  /* PWM + FRC auf 15 Levels  */
  LCD_CMD = 0x88;  /* Graupalette weiß  */
  LCD_CMD = 0;

  LCD_CMD = 0x89;  /* Graupalette ltgr  */
  LCD_CMD = 0;

  LCD_CMD = 0x8A;  /* Graupalette dkgr  */
  LCD_CMD = 0x55;

  LCD_CMD = 0x8B;
  LCD_CMD = 0x55;

  LCD_CMD = 0x8C;
  LCD_CMD = 0xAA;

  LCD_CMD = 0x8D;
  LCD_CMD = 0xAA;

  LCD_CMD = 0x8E;  /* Graupalette schwarz  */
  LCD_CMD = 0xFF;

  LCD_CMD = 0x8F;  /* Graupalette schwarz  */
  LCD_CMD = 0xFF;
  pause();
  FlipDisplay(TRUE);
  LCD_CMD = 0xAF;  /* Display on  */
  FillLanes(white);
  ScrBlt();
}


/**************************************************************/
/* fontbezogenen Funktionen                                   */
/**************************************************************/

/*  liefert die Zeichenhöhe des aktuellen Fonts
    ===========================================
 */
int Get_dY      (PFONT Font)
{ return ((struct Fontheader*) Font)->Height; }


/*  liefert die Höhe über Schreiblinie
    ==================================
 */
int Get_Ascent  (PFONT Font)
{ return ((struct Fontheader*) Font)->Ascent; }


/*  liefert die Tiefe unter der Schreiblinie
    ========================================
 */
int Get_Descent (PFONT Font)
{ return ((struct Fontheader*) Font)->Descent; }


/* liefert die Zeichenbreite des Zeichens
   ======================================
 */
int Get_dX (PFONT Font, char Ch)
{ struct Fontheader* Fh;
  struct CharEntry*  Ce;
  Fh = (struct Fontheader*) Font;
  Ce = (struct CharEntry*) ( (long) Font + Fontheadersize);
  if (Ch < Fh->ChMin) return 0;
  if (Ch > Fh->ChMax) return 0;
  Ce += (Ch - Fh->ChMin);
  return Ce->dX;
}

/* einen Zeiger auf einen Font liefern
   ===================================
*/
PFONT  Get_Font (int Fontnummer)
{ long i;
  i = Fontnummer & 15;
  if (i>=FontAnzahl) i = idf_Helv_12;
  return FontPool[i];
}


/****************************************************************
 Rechteck auf Bildschirm füllen. mode 0..4 (Farbe)
 left/top = erste zu füllende Koordinate
 right/bottom = letzte zu füllende Koordinate.
 also CgFillRect(.. 3,3,3,3) füllt genau 1 Pixel auf Position 3/3
 ****************************************************************/

void CgFillRect  (RECT* R, int mode)
{ int  i, left, top, right, bott;
  int  page, andmask, ormask;

  left = R->left;
  top  = R->top;
  right= R->right;
  bott = R->bottom;
  mode = mode & allmodes;
  if (left < 0)    left  = 0;
  if (right> lcdbreite-1)  right = lcdbreite-1;
  if (top  < 0)    top   = 0;
  if (bott > lcdhoehe-1)   bott  = lcdhoehe-1;
  while (top <= bott)
  {  DirtyPages |= (1<<(top>>3));
     page = (top>>3) * lcdbreite;
     ormask = 1<<(top & 7);
     andmask = ~ormask;

     i = left;
     switch (mode)
     { case black:
            while (i <= right)
            { PixLane1[page+i] |= ormask;
              PixLane2[page+i] |= ormask;
	      ++i;
            }
	    break;

       case dkgr:
            while (i <= right)
            { PixLane1[page+i] |= ormask;
	      PixLane2[page+i] &= andmask;
	      ++i;
            }
	    break;

       case ltgr:
            while (i <= right)
            { PixLane1[page+i] &= andmask;
	      PixLane2[page+i] |= ormask;
	      ++i;
            }
	    break;

       case white:
            while (i <= right)
            { PixLane1[page+i] &= andmask;
	      PixLane2[page+i] &= andmask;
	      ++i;
            }
	    break;


       case invert:
            while (i <= right)
            { PixLane1[page+i] ^= ormask;
              PixLane2[page+i] ^= ormask;
	      ++i;
            }
	    break;
     }
     ++top;
  }
}


/****************************************************************
 Pixel auf Koordinate [x,y] mit Farbe schreiben.
 zurückgeliefert wird Erfolg
 ****************************************************************/
bool CgPixel_at (int X, int Y, int mode)
{ int  page, andmask, ormask;

  if (X < 0)   return 0;
  if (X > lcdbreite-1) return 0;
  if (Y < 0)   return 0;
  if (Y > lcdhoehe-1)  return 0;
  mode = mode & allmodes;
  DirtyPages |= (1<<(Y>>3));
  page = (Y>>3) * lcdbreite + X;
  ormask = 1<<(Y&7);
  andmask = ~ormask;
  switch (mode)
  { case black:
             PixLane1[page] |= ormask;
             PixLane2[page] |= ormask;
 	     return 1;

       case dkgr:
             PixLane1[page] |= ormask;
	     PixLane2[page] &= andmask;
 	     return 1;

       case ltgr:
             PixLane1[page] &= andmask;
	     PixLane2[page] |= ormask;
	     return 1;

       case white:
             PixLane1[page] &= andmask;
	     PixLane2[page] &= andmask;
	     return 1;

       case invert:
             PixLane1[page] ^= ormask;
             PixLane2[page] ^= ormask;
	     return 1;
  }
  return 0;
}


/***************************************************************
 Zeichnet das Textzeichen im aktuellen Font an der
 angegebenen Stelle und liefert die Breite des Zeichens
 ***************************************************************/
int CgCh_at(int X, int Y, char Ch, word FontMode)
{ struct Fontheader* FHE;
  struct CharEntry*  CHE;
  PFONT  Font;
  word   mode;
  byte*  Pix;
  long   top, bott;
  dword  w;
  dword  pixw;
  long   dX;

  mode  = FontMode & allmodes;
  Font  = Get_Font(FontMode);
  top   = Y;

/* aus dem Font die Breite, Höhe und Pixel entnehmen */
  FHE = (struct Fontheader*) Font;
  if ((Ch < FHE->ChMin)||(Ch > FHE->ChMax)) Ch = 0;
  else Ch = Ch - FHE->ChMin;
  CHE = (struct CharEntry*) ((long) FHE + Fontheadersize);
  CHE += Ch;
  w   = (CHE->DispHi << 8) | CHE->DispLo;
  Pix = (byte*) ((long) FHE + w);

/* bott setzen */
  bott = top + CHE->dY;
  if (bott>lcdhoehe-1) bott = lcdhoehe-1;

/* die Register sind jetzt:
  bott = zeigt jetzt hinter die unterste zu schreibende Zeile des Zeichens
  Pix  = Zeiger auf zugehörige Pixel im Font
  FHE, CHE haben sich (fast) erledigt
  Nun die ersten Fontpixel und die Maske laden
 */
  pixw = *Pix++;
  w = 1;

/* Y - Zeichen-Zyklus */
  while (top < bott)
  {   /* X - Zeichen-Zyklus */
      dX  = 0;
      while (dX < CHE->dX)   /* Zeichenbreite in Pixeln */
      { if (pixw & w) CgPixel_at (X+dX, top, mode);
	++dX;
        w = (w << 1) & 0xFF;
        if (!w)
        {   pixw = *Pix++;
            w = 1;
	}
      }
      ++top;
  }
  return CHE->dX;
}


/* berechnet die Breite in Pixeln eines Strings
   ============================================
*/
int StringWidth (char* S, PFONT Font)
{ int i;
  i = 0;
  while (*S) i += Get_dX(Font, *S++);
  return i;
}



/* zeichnet eine Linie
   ===================
*/
void DrawLine (POINT A, POINT B, int mode)
{ int x, y, c;
  int D, HX, HY, M;
  int xInc, yInc;

  x = A.X;
  y = A.Y;
  D = 0;
  HX = B.X - A.X;
  HY = B.Y - A.Y;
  xInc = 1;
  yInc = 1;

  if (HX < 0) { xInc = -1; HX = -HX; };
  if (HY < 0) { yInc = -1; HY = -HY; };
  if (HY <= HX)
  { c = 2 * HX;
    M = 2 * HY;
    loop1:
      CgPixel_at(x,y,mode);
      if (x == B.X) return;
      x = x + xInc;
      D = D + M;
      if (D > HX)
       { y = y + yInc;
         D = D - c;
       }
      goto loop1;
  }
  else
  { c = 2 * HY;
    M = 2 * HX;
    loop2:
      CgPixel_at(x,y,mode);
      if (y == B.Y) return;
      y = y + yInc;
      D = D + M;
      if (D > HY)
       { x = x + xInc;
         D = D - c;
       }
      goto loop2;
  }
}


/* diese Funktionen werden für externe Zugriffe per SWI benötigt */
POINT MerkePoint;

void MoveTo (POINT* A)
{ MerkePoint = *A;
}

void LineTo (POINT* B, int mode)
{ DrawLine (MerkePoint, *B, mode);
  MerkePoint = *B;
}


/***************************************************************
 Zeichnet einen String im aktuellen Font an der
 angegebenen Stelle und liefert die Breite des Strings
 ***************************************************************/
int CgStr_at (int x, int y, char* P, word FontMode)
{ int z;
  while (*P)
  { z = CgCh_at(x,y,*P++, FontMode);
    x = x + z;
  }
  return (x);
}

/**************************************************************
 Zeichnet ein Bild.
 X,Y = Koordinate links oben

 Bildformat:
 zuerst Breite und Höhe als byte
 dann die Pixel als Bitstream, jedes Pixel belegt 2 Bit
 Bit 0 und 1 des ersten Bytes ergeben das allererste Pixel links oben bei x,y
 zuerst wird die oberste Zeile gezeichnet, dann weiter mit der nächsten Zeile
 usw. bis alle Zeilen gezeichnet sind.
 jedes Pixel kann die Werte 0..3 haben, die in bitcolors[4] den Farben
 zugeordnet werden.
***************************************************************/

const word bitcolors[4] = { white, ltgr, dkgr, black };

void CgPicture_at(int x, int y, char* P)
{ int breite;
  int hoehe;
  int i, j;
  int mode;
  byte* Pt;
  int BitPos;
  dword BitBuf;
  dword Q;

  BitBuf = BitPos = 0;
  Pt = (byte*) P;
  breite = *Pt++;
  hoehe  = *Pt++;

  j = 0;
  while (j < hoehe)
  { i = 0;
    while (i < breite)
    { if (BitPos < 2)
      { Q = *Pt++;
        BitBuf = BitBuf | (Q << BitPos);
	BitPos = BitPos + 8;
      }
      mode = BitBuf & 3;
      mode =  bitcolors[mode];
      BitBuf = BitBuf >> 2;
      BitPos = BitPos - 2;
      CgPixel_at(x+i,y+j,mode);
      ++i;
    }
    ++j;
  }
}


/***************************************************************
 Zeichnet einen Kreis um den Punkt A
 (für Pi wird hier einfach 3 angenommen)
 ***************************************************************/
void CgCircle_at (POINT* A, int radius, int mode)
{ int x, X0;
  int y, Y0;
  int d;
  int dx,dxy;

  X0  = A->X;
  Y0  = A->Y;
  x   = 0;
  y   = radius;
  d   = 1-radius;
  dx  = 3;
  dxy = -2*radius + 5;

  while (y>=x)
  { CgPixel_at(X0+x, Y0+y, mode);
    CgPixel_at(X0+y, Y0+x, mode);
    CgPixel_at(X0+y, Y0-x, mode);
    CgPixel_at(X0+x, Y0-y, mode);
    CgPixel_at(X0-x, Y0-y, mode);
    CgPixel_at(X0-y, Y0-x, mode);
    CgPixel_at(X0-y, Y0+x, mode);
    CgPixel_at(X0-x, Y0+y, mode);

    if (d<0)
    { d   = d+dx;
      dx  = dx+2;
      dxy = dxy+2;
      x++;
    }
    else
    { d   = d+dxy;
      dx  = dx+2;
      dxy = dxy+4;
      x++;
      y--;
    }
  }
}



   /* ende */
