/*======================================================
FILE:  AEEVarBitFont.c

SERVICES:  AEEVarBitFont class

GENERAL DESCRIPTION:
   BREW bitmapped variable width font implementation.
   AKA: BBF Version 1
   This is not superceded by version2, but handles variable
   width fonts.

PUBLIC CLASSES AND STATIC FUNCTIONS:

   TBD()

   
INITIALIZATION & SEQUENCING REQUIREMENTS:

	See Exported Routines

        Copyright ?1999-2004 QUALCOMM Incorporated.
               All Rights Reserved.
            QUALCOMM Proprietary/GTDR
=====================================================*/
#include "AEEBitmap.h"
#include "AEEFont.h"
#include "AEEStdLib.h"
#include "AEEBBF.h"
#include "AEEBitFont_priv.h"
#include "AEEBitmap_priv.h"

#define PALLOCREC_EX(pfn, type, extra)   ( (type *) PAlloc((pfn), sizeof(type) + (extra)) )
// Make sure allocated memory is zeroed; BREW realloc didn't always do this,
// and this might be some other realloc funtion anyway.
__inline void *PAlloc(PFNREALLOC pfn, uint32 dw)
{
   void *ptr;

   BRIDLE_ASSERT_SAFE_PFN(pfn);
   ptr = pfn(0, dw);
   if (ptr)
      MEMSET(ptr, 0, dw);
   return ptr;
}

static void *MyRealloc(void *pv, uint32 dwSize)
{
   return REALLOC(pv, dwSize);
}

#ifdef UNIT_TEST

static int TEST_CharToGlyph()
{
   int cntFail = 0;
   AEEBBFRange r[9];

   r[0].wCharBase = 32;   r[0].wCount = 224;
   r[1].wCharBase = 800;  r[1].wCount = 24;
   r[2].wCharBase = 333;  r[2].wCount = AEEBBF_INDEXRESET;    // use glyph index 777
   r[3].wCharBase = 600;  r[3].wCount = 100;
   r[4].wCharBase = 444;  r[4].wCount = AEEBBF_INDEXFREEZE;    // use glyph index 777
   r[5].wCharBase = 500;  r[5].wCount = 100;
   r[6].wCharBase = 400;  r[6].wCount = 100;
   r[7].wCharBase = 555;  r[7].wCount = AEEBBF_INDEXRESET;    // use glyph index 777
   r[8].wCharBase = 700;  r[8].wCount = 100;

   static struct {
      uint16 wChar;
      int result;
   } tests[] = {
      32, 0,         // start of range
      255, 223,      // end of range
      31, -1,        // just before range
      256, -1,       // just after range
      801, 225,      // second range
      610, 343,      // reset range
      510, 444,      // frozen range
      410, 444,      // second frozen range
      710, 565,      // range after reset (after freeze)
      999, -1,       // greater than all
   };


   for (int i = 0; i < ARRAY_SIZE(tests); ++i) {
      if (CharToGlyph(tests[i].wChar, r, 9) != tests[i].result)
         ++cntFail;
   }

   return cntFail;
}

// Unit test
//
int TEST_All()
{
   return TEST_CharToGlyph();
}

#endif // UNIT_TEST



//---- AEEBitFont:  Simple Bitmapped Font Implementation
//
// AEEBitFont uses a 1-bit DIB to represent fonts.
//


// AEEBitFont == IFont (in this .c)
//
// In this module, the IFont structure is defined to be an AEEBitFont, so the
// vtable can typecheck without casting function pointers and without casting
// the "me" pointers inside those functions.
//
struct IFont {
   const AEEVTBL(IFont) *pvt;
   uint32      uRefs;
   PFNREALLOC  pfnRealloc;
   AEEFontInfo fi;                // validated
   AEEBBFRange *pRanges;          // contents not validated; may hold invalid glyph indices
   uint16      wRangeCount;       // validated
   uint16      wHeight;           // safe (not validated)
   uint16      wUndefGlyph;       // validated
   uint16      wGlyphCount;       // safe (bad values impair functionality, not crash)
   IDIB  *     pDIB;
   void *      pvStorage; // pointer to free
   RGBVAL      rgb0;      // current fg color in DIB
   RGBVAL      rgb1;      // current bg color in DIB
   uint32      pal[2];
   boolean     bFlipWidths;   // Endian mismatch for BBF Vsn 2
   uint16 *    pawWidths;  // Pointer to positon data, may point to awPos
   uint16      awPos[1];  // (variable size)     awPos[nGlyph] = x position of left edge
   // ... awPos[1...wGlyphCount] ...
};


static int
AEEBitFont_GetInfo(IFont *me, AEEFontInfo *pInfo, int nSize)
{
   if (sizeof(*pInfo) != nSize)
      return EUNSUPPORTED;
   
   MEMMOVE(pInfo, &me->fi, nSize);

   return SUCCESS;
}


// Return width and position of glyph, given character value
__inline int
AEEBitFont_GetGlyph(IFont *me, unsigned short wch, int *pxChar)
{
   int xChar, widChar;
   int32 ndx = CharToGlyph(wch, me->pRanges, me->wRangeCount);

   // With downloadable fonts, we cannot trust glyph index to be in range
   if ( (uint32)ndx >= me->wGlyphCount)
      ndx = me->wUndefGlyph;

   // get x & width
   
   if( me->bFlipWidths ){
      xChar = SWAPS(me->pawWidths[ndx]);
      widChar = SWAPS(me->pawWidths[ndx+1]) - xChar;
   }else{
      xChar = me->pawWidths[ndx];
      widChar = me->pawWidths[ndx+1] - xChar;
   }

   *pxChar = xChar;
   return widChar;
}


static int
AEEBitFont_MeasureText(IFont *me, const AECHAR *pwcText, int cwText,
                       int nMaxWidth, int *pnFits, int *pnPixels)
{
   int i;
   int wid = 0;

   for (i = 0; i < cwText; ++i) {
      int xChar;
      int widChar = AEEBitFont_GetGlyph(me, pwcText[i], &xChar);

      wid += widChar;
      if (wid > nMaxWidth) {
         wid -= widChar;
         break;
      }
   }

   if (pnFits)
      *pnFits = i;
   if (pnPixels)
      *pnPixels = wid;

   return SUCCESS;
}


static int
AEEBitFont_DrawText(IFont *me, IBitmap *pDst, int x, int y,
                    const AECHAR *pwcText, int nChars, 
                    NativeColor fg, NativeColor bg,
                    const AEERect *prcClip, uint32 dwFlags)
{
   int yBase = 0;               // y index into character bitmap
   int yHeight = me->wHeight;
   int xMin, xMax, yMin, yMax;
   AEERasterOp rop = AEE_RO_COPY;
   int nResult = SUCCESS;
   int i;

   if (dwFlags & IDF_TEXT_TRANSPARENT)
      rop = AEE_RO_TRANSPARENT;

   xMin = prcClip->x;
   yMin = prcClip->y;
   xMax = xMin + prcClip->dx;
   yMax = yMin + prcClip->dy;


   {
      // Update FG and BG colors
      
      RGBVAL rgb0 = IBITMAP_NativeToRGB(pDst, fg);
      RGBVAL rgb1 = IBITMAP_NativeToRGB(pDst, bg);

      if (rgb0 != me->rgb0 ||
          rgb1 != me->rgb1) {

         me->rgb0 = rgb0;
         me->rgb1 = rgb1;

         me->pDIB->pRGB[0] = (rgb0 << 8) & 0x00ff0000 | (rgb0 >> 8) & 0x0000ff00 | (rgb0 >> 24) & 0x000000ff;
         me->pDIB->pRGB[1] = (rgb1 << 8) & 0x00ff0000 | (rgb1 >> 8) & 0x0000ff00 | (rgb1 >> 24) & 0x000000ff;

         // Flush the palette map, because the palette has changed
         IDIB_FlushPalette(me->pDIB);
      }
   }

   
   // Clip bottom of line
   if (y+yHeight > yMax)
      yHeight = yMax - y;

   // Clip top of line
   if (y < yMin) {
      yBase = yMin - y;
      yHeight -= yBase;
      y = yMin;
   }

   if (yHeight <= 0)
   {
      return SUCCESS;          // don't call BltIn() with negative height (just to be nice)
   }
   
   for (i = 0; i < nChars; ++i) {
      // Find character
      int xChar;
      int widChar = AEEBitFont_GetGlyph(me, pwcText[i], &xChar);
         
      // Clip left of character
      if (x < xMin) {

         if (x + widChar <= xMin) {
            // Entire character too far left
            x += widChar;
            continue;
         }
            
         xChar   += xMin - x;
         widChar -= xMin - x;
         x = xMin;
      }

      // Clip right of character
      if (x + widChar > xMax) {
         widChar = xMax - x;
         if (widChar <= 0) {
            // Entire characer too far right
            break;
         }
      }

      nResult = IBITMAP_BltIn(pDst, x, y, widChar, yHeight, (IBitmap*)me->pDIB, xChar, yBase, rop);
      if (nResult != SUCCESS)
         break;

      x += widChar;
   }

   return nResult;
}


static uint32
AEEBitFont_AddRef(IFont *me)
{
   return ++(me->uRefs);
}


static uint32
AEEBitFont_Release(IFont *me)
{
   //ASSERT(me->uRefs);
   if (!me->uRefs)
      return 0;

   if (--me->uRefs > 0)
      return me->uRefs;

   // Ref count is zero, so clean up

   if (me->pDIB)
      IDIB_Release(me->pDIB);

   if (AEEBITFONT_ISHEAPDATA(me->pvStorage)) {
      MYFREE(me, me->pvStorage);
   }

   MYFREE(me, me);

   return 0;
}


static int
AEEBitFont_QueryInterface(IFont *me, AEECLSID id, void **ppif)
{
   void *po = 0;
   
   if (id == AEECLSID_FONT ||
       id == AEECLSID_QUERYINTERFACE) {
      IFONT_AddRef(me);
      po = (void*)me;
   }

   *ppif = po;
   return (po != 0 ? SUCCESS : ECLASSNOTSUPPORT);
}



static const VTBL(IFont) gAEEBitFontFuncs = {
   AEEBitFont_AddRef,
   AEEBitFont_Release,
   AEEBitFont_QueryInterface,
   AEEBitFont_DrawText,
   AEEBitFont_MeasureText,
   AEEBitFont_GetInfo
};


// Read a BBF width array.
//
// For each glyph index, ndx, pwPos[ndx] is set to the left boundary of the
// character cell, and pwPos[ndx+1] is set to the right boundary.  Total size
// of pwPos[] is wGlyphCount+1 elements.
//
static int ReadWidths(uint16 *pwPos, int wGlyphCount, const byte *pbyWidth, int cbWidths)
{
   const byte *pbyEnd = pbyWidth + cbWidths;
   int ndx = 0;              // over awPos[wGlyphCount+1]
   int nCurrentWidth = 0;
   int nTotalWidth = 0;
   
   pwPos[0] = 0;

   while (ndx < wGlyphCount) {
      int nReps = 1;
      int nWidth = 0;

      if (pbyWidth < pbyEnd)
         nWidth = *pbyWidth++;
      
      if (nWidth < AEEBBF_REPBASE) {
         nCurrentWidth = nWidth;
      } else {
         // repeat
         if (nWidth <= AEEBBF_REP32BASE) {
            nReps = nWidth - AEEBBF_REPBASE;
         } else if (nWidth <= AEEBBF_REP32BASE + 32) {
            nReps = (nWidth - AEEBBF_REP32BASE) * 32;
         } else {
            // error: ignore
            nReps = 0;
         }
      }
      
      while (nReps > 0 && ndx < wGlyphCount) {
         nTotalWidth += nCurrentWidth;
         pwPos[ndx+1] = nTotalWidth;
         ++ndx;
         --nReps;
      }
   }

   // bad file could overflow 16-bit total width
   return (nTotalWidth > 65535 ? EFAILED : SUCCESS); 
}



// AEEBitFont_New : Create a new IFont, given a font bitmap and font descent
//
// On entry:
//
//   pbyMem[cbMem] = source data (BBF format)
//
//   pvStorage : controls how the memory in pbyMem[] is treated.  It will hold
//      one of the following values:
//
//      1. pointer to memory block => the object can retain pointers into
//         pbyMem[] instead of copying out the data.  If the object is
//         successfully creates, it will free this pointer using FREE() at
//         some point.  If creation fails, this pointer will not be freed.
//
//      2. AEEBITFONT_COPYDATA => This function must copy any data it needs
//         from pbyMem[] before returning.
//
//      3. AEEBITFONT_STATICDATA => The IFont will retain pointers into
//         pbyMem[].  It does not need to free anything.
//
// On success:
//
//   Return value is SUCCESS and *ppIFont will hold the IFont pointer.
//
// On failure:
//
//   Return value = error code.  *ppIFont will hold NULL.  Any pointer
//   passed as pvStorage will NOT be freed.
//
//      ENOMEMORY
//      EINVALIDFORMAT
//
//           

int AEEVarBitFont_NewFromBBF(const byte *pbyMem, int cbMem, void *pvStorage, PFNREALLOC pfnRealloc, IFont **ppIFont)
{
   IFont *me;
   IDIB *pDIB;
   int nResult;
   AEEBBFHdr hdr;
   const byte *pbyBitmap;
   AEEBBFRange *pRanges;
   int cbExtra, cbBitmap, cbRanges, cbRow, nRangeOffset, nWidthsOffset, nBitmapOffset;
   boolean bCopyData;

   if (!ppIFont)
      return EBADPARM;

   *ppIFont = NULL;

   if (!pfnRealloc)
      pfnRealloc = MyRealloc;

   //
   // Examine BBF data
   //

   if (cbMem < sizeof(hdr))
      return EINVALIDFORMAT;

   MEMCPY(&hdr, pbyMem, sizeof(hdr));

   cbRanges = hdr.wRangeCount * sizeof(AEEBBFRange);
   nRangeOffset = sizeof(AEEBBFHdr) + ROUNDUP4(hdr.cbAdditional);
   nWidthsOffset = nRangeOffset + cbRanges;
   if( !hdr.byVersion ){
      nBitmapOffset = nWidthsOffset + ROUNDUP4(hdr.cbWidths);
   }else{
      // The <glyphwidths> section is expanded to uint16 array for this version
      nBitmapOffset = nWidthsOffset + ROUNDUP4((hdr.cbWidths<<1));
   }

   cbRow = ROUNDUP32(hdr.wWidth) >> 3;
   cbBitmap = cbRow * hdr.wHeight;
   pRanges = (AEEBBFRange*) (pbyMem + nRangeOffset);
   pbyBitmap = pbyMem + nBitmapOffset;

   if ((hdr.byVersion != 0 && hdr.byVersion != 1) ||
       nBitmapOffset + cbBitmap > cbMem)
      return EINVALIDFORMAT;

   //
   // Allocate me + widths [ + bitmap]
   //

   if( !hdr.byVersion ){
      cbExtra = hdr.wGlyphCount * sizeof(uint16);   // awPos[] array size == wGlyphCount + 1
   }else{
      cbExtra = 0;
   }
      
   // Copy all data if user requires or if memory is not properly aligned
   
   bCopyData = (pvStorage == AEEBITFONT_COPYDATA || ((int32)pbyMem & 3) != 0);

   if (bCopyData) {
      // copy bitmap data as well
      cbExtra = ROUNDUP4(cbExtra) + cbRanges + cbBitmap;
   }
   
   BRIDLE_ASSERT_SAFE_PFN(pfnRealloc);
   me = PALLOCREC_EX(pfnRealloc, IFont, cbExtra);
   if (!me)
      return ENOMEMORY;

   me->pvt = &gAEEBitFontFuncs;
   me->uRefs = 1;
   me->pfnRealloc = pfnRealloc;

   if (bCopyData) {
      // Copy bitmap data into me
      byte *pby = (byte*)(me+1) + cbExtra - cbBitmap;
      MEMMOVE(pby, pbyBitmap, cbBitmap);
      pbyBitmap = pby;

      // Copy range data into me
      pby = (byte*)pbyBitmap - cbRanges;
      MEMMOVE(pby, pRanges, cbRanges);
      pRanges = (AEEBBFRange*) pby;
   }

   //
   // Create and setup DIB
   //

   nResult = DIB_NewEx(1, 0, 0, 0, pfnRealloc, &me->pDIB);
   if (nResult != SUCCESS) {
      AEEBitFont_Release(me);
      return ENOMEMORY;
   }

   pDIB = me->pDIB;
   pDIB->cx = hdr.wWidth;
   pDIB->cy = hdr.wHeight;
   pDIB->pBmp = (byte*)pbyBitmap + cbBitmap - cbRow;   // point to row 0
   pDIB->nPitch = -cbRow;
   pDIB->pRGB = me->pal;
   pDIB->cntRGB = 2;
   pDIB->ncTransparent = 1;    // palette index (DIB native color value)

   //
   // Other initialization
   //
   
   me->wHeight     = hdr.wHeight;
   me->fi.nDescent = MIN(hdr.byDescent, hdr.wHeight);
   me->fi.nAscent  = hdr.wHeight - hdr.byDescent;
   me->wGlyphCount = hdr.wGlyphCount;
   me->wUndefGlyph = MIN(hdr.wUndefGlyph, hdr.wGlyphCount-1);

   me->pal[0] = 0;              // black uint32
   me->pal[1] = 0x00FFFFFF;     // white uint32
   me->rgb0 = 0;                // black RGBVAL
   me->rgb1 = 0xFFFFFF00;       // white RGBVAL = NTOHL(uint32)

   //
   // Read widths and construct awPos[] array
   //
   me->bFlipWidths = FALSE;
   if( !hdr.byVersion ){
      if (ReadWidths(me->awPos, hdr.wGlyphCount, pbyMem + nWidthsOffset, hdr.cbWidths) != SUCCESS) {
         AEEBitFont_Release(me);
         return EINVALIDFORMAT;
      }
      me->pawWidths = me->awPos;
   }else{
      me->pawWidths = (uint16 *)(pbyMem + nWidthsOffset);
      if( ((hdr.byFlags&AEEBBF_BEWIDTHS) && 1 == LETOHS(1))
         || (!(hdr.byFlags&AEEBBF_BEWIDTHS) && 1 != LETOHS(1)) ){
         me->bFlipWidths = TRUE;
      }
   }

   //
   // Read ranges
   //

   me->pRanges = pRanges;
   me->wRangeCount = hdr.wRangeCount;

   //
   // Done
   //

   *ppIFont = me;
   me->pvStorage = pvStorage;     // if we return success, we may have to free this
   
   return SUCCESS;
}
