/*======================================================
FILE:  AEEMonoBitFont.c

SERVICES:  AEEMonoBitFont class

GENERAL DESCRIPTION:
   BREW bitmapped monospace font implementation.
   AKA: BBF Version 2
   This is not a replacement for version 1, but a different
   flavor for handling large fixed width font implemntations.

PUBLIC CLASSES AND STATIC FUNCTIONS:

   TBD()

   
INITIALIZATION & SEQUENCING REQUIREMENTS:

	See Exported Routines

        Copyright ?1999-2004 QUALCOMM Incorporated.
               All Rights Reserved.
            QUALCOMM Proprietary/GTDR
=====================================================*/
#include "AEEStdLib.h"
#include "AEEDisp.h"     // for IDF_FONT_TRANSPARENT
#include "AEEFont.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);
}

// 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];
   AEEMonoData mdData;           // Use Reserved member to copy character width for quicker access
};

//----------------------------------------------------------------
//  Private functions
//----------------------------------------------------------------

//
#define RGBVAL_TO_RGB(r)    (((r) << 8) & 0x00ff0000 | ((r) >> 8) & 0x0000ff00 | ((r) >> 24) & 0x000000ff)

// TRUE if ch is a half-width character, given the number of half-width characters
#define IS_HALF_WIDTH(ch, cntHalf)                ( ((unsigned)(ch)) < (unsigned) cntHalf )

// Returns width of the character
#define CHAR_WIDTH(ch, cntHalf, xFullWidth)       ( IS_HALF_WIDTH(ch, cntHalf) ? ((xFullWidth)>>1) : (xFullWidth) )


// Set the palette entries to draw the appropriate fg/bg colors.
//
// Note that fg & bg are given as NativeColors for the destination bitmap.  We
// must translate this to the appropriate RGB values to be used in an RGB
// palette.  Each call to DrawText() could be passed a different IBitmap, so
// we cannot cache the translated values.
//
// Also: try to minimize the number of times we modify the palette, becuase that
// introduces the need to perform IDIB_FlushPalette().
//
//
static __inline void
AEEMonoFont_SetPalette(IFont *me, IBitmap *pDst, NativeColor fg, NativeColor bg)
{
   RGBVAL rgb0 = IBITMAP_NativeToRGB(pDst, fg);
   RGBVAL rgb1 = IBITMAP_NativeToRGB(pDst, bg);
   uint32 u0   = RGBVAL_TO_RGB(rgb0);
   uint32 u1   = RGBVAL_TO_RGB(rgb1);

   uint32 *pRGB = me->pDIB->pRGB;

   if (u0 != pRGB[0] ||
       u1 != pRGB[1]) {

      pRGB[0] = RGBVAL_TO_RGB(rgb0);
      pRGB[1] = RGBVAL_TO_RGB(rgb1);

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


// Find the x & y of the character cell
//
static __inline void
AEEMonoFont_LocateGlyphFromRange(IFont *me, uint16 wChar, int *pxChar, int *pyChar)
{
   int32          wGlyph      = CharToGlyph(wChar, me->pRanges, me->wRangeCount);

   if( wGlyph == -1 ){
      wGlyph = me->wUndefGlyph;
   }
   *pxChar = (wGlyph & 0xFF) * me->mdData.wReserved; // wReserved = Stored copy of Width
   *pyChar = (wGlyph >> 8) * me->wHeight;
}


//----------------------------------------------------------------
//  Public functions
//----------------------------------------------------------------


static uint32
AEEMonoFont_AddRef(IFont *pIFont)
{
   return ++(pIFont->uRefs);
}


static uint32
AEEMonoFont_Release(IFont *pIFont)
{
   if (!pIFont->uRefs)
      return 0;

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

   // Ref count is zero, so clean up

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

   FREE(pIFont);

   return 0;
}


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

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



static int
AEEMonoFont_GetInfo(IFont *pIFont, AEEFontInfo *pInfo, int nSize)
{

   if (sizeof(AEEFontInfo) != nSize)
      return EUNSUPPORTED;
   
   MEMCPY((void *)pInfo, (void *)&pIFont->fi, sizeof(AEEFontInfo));

   return SUCCESS;
}

static int
AEEMonoFont_MeasureText(IFont *pIFont, const AECHAR *pwcText, int cwText,
                       int nMaxWidth, int *pnFits, int *pnPixels)
{
   int xWidth = pIFont->mdData.wReserved;
   int cntHalf = pIFont->mdData.wHalfWidths;
   int wid = 0;
   int ii;

   for (ii = 0; ii < cwText; ++ii) {

      int widChar = CHAR_WIDTH(pwcText[ii], cntHalf, xWidth);

      wid += widChar;

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

   *pnFits   = ii;
   *pnPixels = wid;
   
   return SUCCESS;
}


static int
AEEMonoFont_DrawText(IFont *pIFont, IBitmap *pDst, int x, int y,
                    const AECHAR *pwcText, int cwText, 
                    NativeColor fg, NativeColor bg,
                    const AEERect *prcClip, uint32 dwFlags)
{
   AEERasterOp rop = AEE_RO_COPY;
   int xMin, xMax, yMin, yMax;
   int xWidth  = pIFont->mdData.wReserved;
   int cntHalf = pIFont->mdData.wHalfWidths;
   int yHeight = pIFont->wHeight;
   int yOffset = 0;               // y index into character bitmap
   int nResult = SUCCESS;
   int ii;

   AEEMonoFont_SetPalette(pIFont, pDst, fg, bg);
   
   if (dwFlags & IDF_TEXT_TRANSPARENT) {
      rop = AEE_RO_TRANSPARENT;
   }

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

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

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

   if (yHeight <= 0) {
      return SUCCESS;          // don't call BltIn() with negative height (just to be nice)
   }
   

   // Draw characters
   //
   // Invariants:
   //   ii = array index of next character
   //   x  = x coordinate of next character
      
   
   // skip chars clipped on left
   for (ii = 0; ii < cwText; ++ii) {
      
      int widChar = CHAR_WIDTH(pwcText[ii], cntHalf, xWidth);

      x += widChar;

      if (x > xMin) {
         x -= widChar;
         break;
      }
   }


   for (; ii < cwText && x < xMax; ++ii) {

      int xChar, yChar;
      AECHAR wch = pwcText[ii];

      int widChar = CHAR_WIDTH(pwcText[ii], cntHalf, xWidth);
         
      AEEMonoFont_LocateGlyphFromRange(pIFont, wch, &xChar, &yChar);

      // Clip left side of character
      if (x < xMin) {
         xChar   += xMin - x;
         widChar -= xMin - x;
         x = xMin;
      }

      // Clip right side of character
      if (x + widChar > xMax) {
         widChar = xMax - x;
      }

      nResult = IBITMAP_BltIn(pDst, x, y, widChar, yHeight, (IBitmap*)pIFont->pDIB, xChar, yChar+yOffset, rop);
      if (nResult != SUCCESS)
         break;

      x += widChar;
   }

   return nResult;
}

static const VTBL(IFont) gAEEMonoFontFuncs = {
   AEEMonoFont_AddRef,
   AEEMonoFont_Release,
   AEEMonoFont_QueryInterface,
   AEEMonoFont_DrawText,
   AEEMonoFont_MeasureText,
   AEEMonoFont_GetInfo
};


int AEEMonoBitFont_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;
   nBitmapOffset = nWidthsOffset + ROUNDUP4(hdr.cbWidths);

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

   if( hdr.byVersion != 2 ||
       nBitmapOffset + cbBitmap > cbMem ||
       hdr.cbAdditional != ROUNDUP4(sizeof(AEEMonoData)) ){
      return EINVALIDFORMAT;
   }
   //
   // Allocate me + widths [ + bitmap]
   //

   cbExtra = 0; // Don't need to allocate extra width entries

   // 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 = &gAEEMonoFontFuncs;
   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) {
      AEEMonoFont_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
   //
   {
      //
      // Finding the character height based on bitmap and number of glyphs
      //
      int nDivisor = hdr.wGlyphCount>>8;
      if( (hdr.wGlyphCount&0xFF) ){
         nDivisor++;
      }
      me->wHeight = hdr.wHeight/nDivisor;
   }
   me->fi.nDescent = MIN(hdr.byDescent, me->wHeight);
   me->fi.nAscent  = me->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 Half-Widths and Widths entry
   //

   me->mdData.wHalfWidths = *((uint16 *)(pbyMem + sizeof(AEEBBFHdr)));
   me->mdData.wReserved = *((uint16 *)(pbyMem + nWidthsOffset));

   //
   // 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 AEE_SUCCESS;
}
