/*
 * PROJECT:         ReactOS win32 subsystem
 * LICENSE:         GPL - See COPYING in the top level directory
 * PURPOSE:         GDI font driver for bitmap fonts
 * PROGRAMMER:      Timo Kreuzer (timo.kreuzer@reactos.org)
 */

#include "bmfd.h"

ULONG
FORCEINLINE
_ReadPixel(
    CHAR* pjBits,
    ULONG x,
    ULONG y,
    ULONG ulHeight)
{
    CHAR j;
    j = pjBits[(x/8) * ulHeight + y];
    return (j >> (~x & 0x7)) & 1;
}


VOID
FORCEINLINE
_WritePixel(
    CHAR* pjBits,
    ULONG x,
    ULONG y,
    ULONG cjRow,
    ULONG color)
{
    pjBits += y * cjRow;
    pjBits += x / 8;
    *pjBits |= color << (~x & 0x7);
}


PBMFD_FONT
BmfdGetFontInstance(
    FONTOBJ *pfo,
    PBMFD_FACE pface)
{
    PBMFD_FONT pfont = pfo->pvProducer;
    XFORMOBJ *pxo;
    FLOATOBJ_XFORM xfo;

    if (!pfont)
    {
        /* Allocate realization info */
        pfont = EngAllocMem(0, sizeof(BMFD_FONT), 0);
        if (!pfont)
        {
            return NULL;
        }

        pxo = FONTOBJ_pxoGetXform(pfo);
        XFORMOBJ_iGetFloatObjXform(pxo, &xfo);

        pfont->pfo = pfo;
        pfont->pface = pface;
        pfont->xScale = FLOATOBJ_GetLong(&xfo.eM11);
        pfont->yScale = FLOATOBJ_GetLong(&xfo.eM22);
        pfont->ulAngle = 0;

        /* Set the pvProducer member of the fontobj */
        pfo->pvProducer = pfont;
    }

    return pfont;
}


ULONG
BmfdQueryGlyphAndBitmap(
    PBMFD_FONT pfont,
    HGLYPH hg,
    GLYPHDATA *pgd,
    GLYPHBITS *pgb,
    ULONG cjSize)
{
    PBMFD_FACE pface = pfont->pface;
    PGLYPHENTRY pge;
    ULONG xSrc, ySrc, cxSrc, cySrc;
    ULONG xDst, yDst, cxDst, cyDst;
    ULONG xScale, yScale;
    ULONG ulGlyphOffset, cjDstRow, color;
    PVOID pvSrc0, pvDst0;

    /* The glyph handle is the byte offset to the glyph in the table */
    pge = (PGLYPHENTRY)(pface->pCharTable + hg);

    /* Get the bitmap offset depending on file version */
    if (pface->ulVersion >= 0x300)
    {
        cxSrc = GETVAL(pge->ge20.geWidth);
        ulGlyphOffset = GETVAL(pge->ge30.geOffset);
    }
    else
    {
        cxSrc = GETVAL(pge->ge30.geWidth);
        ulGlyphOffset = GETVAL(pge->ge20.geOffset);
    }
    cySrc = pface->wPixHeight;

    /* Pointer to the bitmap bits */
    pvSrc0 = (PBYTE)pface->pFontInfo + ulGlyphOffset;
    pvDst0 = pgb->aj;

    xScale = pfont->xScale;
    yScale = pfont->yScale;

    /* Calculate extents of destination bitmap */
    if (pfont->ulAngle == 90 || pfont->ulAngle == 270)
    {
        cxDst = cySrc * xScale;
        cyDst = cxSrc * yScale;
    }
    else
    {
        cxDst = cxSrc * yScale;
        cyDst = cySrc * xScale;
    }
    cjDstRow = (cxDst + 7) / 8;

    if (pgd)
    {
        /* Fill GLYPHDATA structure */
        pgd->gdf.pgb = pgb;
        pgd->hg = hg;
        pgd->fxD = xScale * (pface->wA + cxDst + pface->wC) << 4;
        pgd->fxA = xScale * pface->wA << 4;
        pgd->fxAB = xScale * (pface->wA + cxDst) << 4;
        pgd->fxInkTop = yScale * pface->wAscent << 4;
        pgd->fxInkBottom = - yScale * (pface->wDescent << 4);
        pgd->rclInk.top = - yScale * pface->wAscent;
        pgd->rclInk.bottom = yScale * pface->wDescent;
        pgd->rclInk.left = xScale * pface->wA;
        pgd->rclInk.right = pgd->rclInk.left + cxDst;
        pgd->ptqD.x.LowPart = 0;
        pgd->ptqD.x.HighPart = pgd->fxD;
        pgd->ptqD.y.LowPart = 0;
        pgd->ptqD.y.HighPart = 0;
    }

    if (pgb)
    {
        /* Verify that the buffer is big enough */
        if (cjSize < FIELD_OFFSET(GLYPHBITS, aj) + cyDst * cjDstRow)
        {
            DbgPrint("Buffer too small (%ld), %ld,%ld\n",
                     cjSize, cxSrc, cySrc);
            return FD_ERROR;
        }

        /* Fill GLYPHBITS structure */
        pgb->ptlOrigin.x = yScale * pface->wA;
        pgb->ptlOrigin.y = - yScale * pface->wAscent;
        pgb->sizlBitmap.cx = cxDst;
        pgb->sizlBitmap.cy = cyDst;

        /* Erase destination surface */
        memset(pvDst0, 0, cyDst * cjDstRow);

        switch (pfont->ulAngle)
        {
            case 90:
                /* Copy pixels */
                for (yDst = 0; yDst < cyDst ; yDst++)
                {
                    xSrc = yDst / yScale;
                    for (xDst = 0; xDst < cxDst; xDst++)
                    {
                        ySrc = (cxDst - xDst) / xScale;
                        color = _ReadPixel(pvSrc0, xSrc, ySrc, cySrc);
                        _WritePixel(pvDst0, xDst, yDst, cjDstRow, color);
                    }
                }
                break;

            case 180:
                for (yDst = 0; yDst < cyDst ; yDst++)
                {
                    ySrc = (cyDst - yDst) / yScale;
                    for (xDst = 0; xDst < cxDst; xDst++)
                    {
                        xSrc = (cxDst - xDst) / xScale;
                        color = _ReadPixel(pvSrc0, xSrc, ySrc, cySrc);
                        _WritePixel(pvDst0, xDst, yDst, cjDstRow, color);
                    }
                }
                break;

            case 270:
                for (yDst = 0; yDst < cyDst ; yDst++)
                {
                    xSrc = (cyDst - yDst) / yScale;
                    for (xDst = 0; xDst < cxDst; xDst++)
                    {
                        ySrc = xDst / xScale;
                        color = _ReadPixel(pvSrc0, xSrc, ySrc, cySrc);
                        _WritePixel(pvDst0, xDst, yDst, cjDstRow, color);
                    }
                }
                break;

            case 0:
            default:
                for (yDst = 0; yDst < cyDst ; yDst++)
                {
                    ySrc = yDst / yScale;
                    for (xDst = 0; xDst < cxDst; xDst++)
                    {
                        xSrc = xDst / xScale;
                        color = _ReadPixel(pvSrc0, xSrc, ySrc, cySrc);
                        _WritePixel(pvDst0, xDst, yDst, cjDstRow, color);
                    }
                }
        }
    }

    /* Return the size of the GLYPHBITS structure */
    return FIELD_OFFSET(GLYPHBITS, aj) + cyDst * cjDstRow;
}

ULONG
BmfdQueryMaxExtents(
    PBMFD_FONT pfont,
    PFD_DEVICEMETRICS pfddm,
    ULONG cjSize)
{
    ULONG cjMaxWidth, cjMaxBitmapSize;
    PFONTINFO16 pFontInfo;
    ULONG xScale, yScale;

    if (pfddm)
    {
        if (cjSize < sizeof(FD_DEVICEMETRICS))
        {
            /* Not enough space, fail */
            return FD_ERROR;
        }

        pFontInfo = pfont->pface->pFontInfo;

        xScale = pfont->xScale;
        yScale = pfont->yScale;

        /* Fill FD_DEVICEMETRICS */
        pfddm->flRealizedType = FDM_MASK;
        pfddm->pteBase.x = FLOATL_1;
        pfddm->pteBase.y = 0;
        pfddm->pteSide.x = 0;
        pfddm->pteSide.y = FLOATL_1;
        pfddm->ptlUnderline1.x = 0;
        pfddm->ptlUnderline1.y = 1;
        pfddm->ptlStrikeout.x = 0;
        pfddm->ptlStrikeout.y = -4;
        pfddm->ptlULThickness.x = 0;
        pfddm->ptlULThickness.y = 1;
        pfddm->ptlSOThickness.x = 0;
        pfddm->ptlSOThickness.y = 1;
        pfddm->lMinA = 0;
        pfddm->lMinC = 0;
        pfddm->lMinD = 0;

        if (pfont->ulAngle == 90 || pfont->ulAngle == 270)
        {
            pfddm->cxMax = xScale * GETVAL(pFontInfo->dfPixHeight);
            pfddm->cyMax = yScale * GETVAL(pFontInfo->dfMaxWidth);
            pfddm->fxMaxAscender = yScale * GETVAL(pFontInfo->dfAscent) << 4;
            pfddm->fxMaxDescender = (pfddm->cyMax << 4) - pfddm->fxMaxAscender;
        }
        else
        {
            pfddm->cxMax = xScale * GETVAL(pFontInfo->dfMaxWidth);
            pfddm->cyMax = yScale * GETVAL(pFontInfo->dfPixHeight);
            pfddm->fxMaxAscender = yScale * GETVAL(pFontInfo->dfAscent) << 4;
            pfddm->fxMaxDescender = (pfddm->cyMax << 4) - pfddm->fxMaxAscender;
        }

        pfddm->lD = pfddm->cxMax;

        /* Calculate Width in bytes */
        cjMaxWidth = ((pfddm->cxMax + 7) >> 3);

        /* Calculate size of the bitmap, rounded to DWORDs */
        cjMaxBitmapSize = ((cjMaxWidth * pfddm->cyMax) + 3) & ~3;

        /* cjGlyphMax is the full size of the GLYPHBITS structure */
        pfddm->cjGlyphMax = FIELD_OFFSET(GLYPHBITS, aj) + cjMaxBitmapSize;

        /* NOTE: fdxQuantized and NonLinear... stay unchanged */
    }

    /* Return the size of the structure */
    return sizeof(FD_DEVICEMETRICS);
}


/** Public Interface **********************************************************/

PFD_GLYPHATTR
APIENTRY
BmfdQueryGlyphAttrs(
    FONTOBJ *pfo,
    ULONG iMode)
{
    DbgPrint("BmfdQueryGlyphAttrs()\n");
    /* We don't support FO_ATTR_MODE_ROTATE */
    return NULL;
}

LONG
APIENTRY
BmfdQueryFontData(
    DHPDEV dhpdev,
    FONTOBJ *pfo,
    ULONG iMode,
    HGLYPH hg,
    OUT GLYPHDATA *pgd,
    PVOID pv,
    ULONG cjSize)
{
    PBMFD_FILE pfile = (PBMFD_FILE)pfo->iFile;
    PBMFD_FACE pface = &pfile->aface[pfo->iFace - 1];
    PBMFD_FONT pfont= BmfdGetFontInstance(pfo, pface);

    DbgPrint("BmfdQueryFontData(pfo=%p, iMode=%ld, hg=%p, pgd=%p, pv=%p, cjSize=%ld)\n",
             pfo, iMode, hg, pgd, pv, cjSize);
//    __debugbreak();

    switch (iMode)
    {
        case QFD_GLYPHANDBITMAP: /* 1 */
            return BmfdQueryGlyphAndBitmap(pfont, hg, pgd, pv, cjSize);

        case QFD_MAXEXTENTS: /* 3 */
            return BmfdQueryMaxExtents(pfont, pv, cjSize);

            /* we support nothing else */
        default:
            return FD_ERROR;

    }

    return FD_ERROR;
}
