If you have comments or questions concerning this source file, discuss them in the forum.
/*
Copyright (c) 2002 Nicolai Haehnle

See the license.txt for details. If that file was not included in the
source distributions, please email <prefect@rtts.org>
*/
// hal_common.cpp - base class implementations

#include "library.h"
#include "hal.h"

#include "hal_sdlgl.h"

HAL *hal;

/*
==============================================================================

HPicture IMPLEMENTATION

==============================================================================
*/

HPicture *HPicture::m_pList = 0;

/*
==============
HPicture::HPicture

Initialize the picture structure with zero values
==============
*/
HPicture::HPicture(const char *name, int type, u32 colorkey)
{
#ifdef DEBUG
//  L_Printf("Create %s\n", name);
#endif

    xstrcpy(m_szName, sizeof(m_szName), name);
    m_iType = type;
    m_colorkey = colorkey;

    m_pImpl = 0;
    bitmask = 0;

    m_iUsed = 1;
    m_iCacheCycle = HAL::cachecycle;

    m_ppPrev = &m_pList;
    m_pNext = m_pList;
    if (m_pNext)
        m_pNext->m_ppPrev = &m_pNext;
    m_pList = this;
}

/*
==============
HPicture::~HPicture

Free allocated memory
==============
*/
HPicture::~HPicture()
{
    if (m_pImpl)
        hal->UnloadPicture(this);

    if (m_pNext)
        m_pNext->m_ppPrev = m_ppPrev;
    *m_ppPrev = m_pNext;
}

/*
==============
HPicture::Get

Lookup the picture in the cache or load it
==============
*/
HPicture *HPicture::Get(const char *name, int type, u32 colorkey)
{
    HPicture *pic;

    for(pic = m_pList; pic; pic = pic->m_pNext) {
        if (!strcmp(pic->m_szName, name)) {
            pic->AddRef();
            pic->m_iCacheCycle = HAL::cachecycle;
            return pic;
        }
    }

    pic = new HPicture(name, type, colorkey);
    try {
        hal->LoadPicture(pic);
    } catch(...) {
        delete pic;
        throw;
    }

    return pic;
}

/*
==============
HPicture::CacheCycle

Evict unused pictures
==============
*/
void HPicture::CacheCycle()
{
    HPicture *next, *pic;

    next = m_pList;
    while(next) {
        pic = next;
        next = next->m_pNext;

        if (pic->m_iUsed)
            pic->m_iCacheCycle = HAL::cachecycle;
        else {
            if (HAL::cachecycle-pic->m_iCacheCycle > 2)
                delete pic;
        }
    }
}

/*
==============
HPicture::UnloadAll

This is called before a HAL change
==============
*/
void HPicture::UnloadAll()
{
    HPicture *pic;

    for(pic = m_pList; pic; pic = pic->m_pNext)
        hal->UnloadPicture(pic);
}

/*
==============
HPicture::LoadAll

Called after a HAL change
==============
*/
void HPicture::LoadAll()
{
    HPicture *pic;

    for(pic = m_pList; pic; pic = pic->m_pNext)
        hal->LoadPicture(pic);
}

/*
==============
HPicture::Cleanup

Complain about potential picture leaks, free all cached pics
==============
*/
void HPicture::Cleanup()
{
    while(m_pList) {
        if (m_pList->m_iUsed)
            L_Printf("%s: picture leak\n", m_pList->m_szName);

        delete m_pList;
    }
}


/*
==============================================================================

COMMON HAL FUNCTIONS

==============================================================================
*/

int HAL::cachecycle = 0;

/*
==============
HAL::HAL
==============
*/
HAL::HAL()
{
    scrsize[0] = 0;
    scrsize[1] = 0;
    framestart = 0;
    frametime = 0;
}

HAL::~HAL()
{
}

/*
==============
HAL::CacheCycle

Call subsystems' CacheCycle functions
==============
*/
void HAL::CacheCycle()
{
    cachecycle++;

    SPR_CacheCycle();
    HFont::CacheCycle();
    HPicture::CacheCycle();
}

#include "keynames.h"

/*
==============
HAL_NameForKey

Use the keynames lookup table
==============
*/
const char *HAL_NameForKey(int code)
{
    keynamecode_t *kc;

    for(kc = keynames; kc->name; kc++)
        if (kc->code == code)
            return kc->name;

    return "bad";
}

/*
==============
HAL_Start

Load a HAL, with parameters read from cfg.
Can also be used to replace the current hal with a new one.
==============
*/
void HAL_Start(LConfig *cfg)
{
    bool cycle;

    if (hal) {
        hal->CacheCycle(); // to reduce potentially unnecessary re-loading time

        HPicture::UnloadAll();
        hal->Shutdown();
        delete hal;
        hal = 0;
        cycle = true;
    } else
        cycle = false;

    hal = new HAL_SdlGl(cfg);
    hal->Init();

    if (cycle)
        HPicture::LoadAll();
}

/*
==============
HAL_Shutdown

Closes the HAL down, frees all resources
==============
*/
void HAL_Shutdown()
{
    SPR_Cleanup();
    HFont::Cleanup();
    HPicture::Cleanup();

    hal->Shutdown();
    delete hal;
    hal = 0;
}

/*
==============
HAL_Screenshot

Automatically get a screenshot and save it in an xxxx????.bmp,
where ???? is an integer, starting with 0000
==============
*/
void HAL_Screenshot(const char *basename)
{
    char path[MAX_OSPATH];
    int num;
    int size[2];
    byte *data;

    num = 0;
    for(;;) {
        snprintf(path, sizeof(path), "%s%04i.bmp", basename, num);
        if (!L_FileExists(path))
            break;
        num++;
    }

    data = hal->Screenshot(size);

    try {
        WriteBMP(path, size, 0, data, false);
    } catch(...) {
        L_Free(data);
        throw;
    }

    L_Free(data);
}

/*
==============
HAL_CanonicalBitmapLoad

Load any bitmap as 32 bit with colorkey etc... applied
==============
*/
byte *HAL_CanonicalBitmapLoad(const char *fname, int *size, int *ptype,
                                 unsigned int colorkey)
{
    char path[MAX_OSPATH];
    byte *palette, *pixels, *scan;
    int pxls, i;
    int x, y;
    int type;

    type = *ptype;

    // Load the bitmap
    snprintf(path, sizeof(path), "%s.bmp", fname);
    LoadBMP(path, size, &palette, &pixels);

    pxls = size[0] * size[1];

    // Convert 8 bit to 32 bit
    if (palette) {
        u32 *buf = (u32 *)L_Malloc(4 * pxls, TAG_HAL);

        for(i = 0; i < pxls; i++) {
            byte idx = pixels[i];

            if (type & __pic_palkey && idx == 255) // transparent
                buf[i] = 0;
            else
                buf[i] = ((u32 *)palette)[idx];
        }

        L_Free(palette);
        L_Free(pixels);
        pixels = (byte *)buf;
    }

    // Check the transparency settings
    type &= ~__pic_alpha;
    type |= __pic_maskonly;

    scan = pixels;
    for(i = 0; i < pxls; i++, scan += 4) {
        if (type & __pic_colorkey) {
            if ((*(unsigned int *)scan & 0xffffff) == colorkey)
                scan[3] = 0;
        }

        if (type & __pic_redalpha) {
            scan[3] = scan[0];
            scan[0] = scan[1] = scan[2] = 255;
        }

        if (scan[3] != 255) {
            type |= __pic_alpha;
            if (scan[3] != 0)
                type &= ~__pic_maskonly;
        }
    }

    // Fill the transparent border pixels with the bordering color
    // This is to avoid a pink color at the border of objects with
    // subpixel-accurate rendering
    if ((type & (__pic_alpha|__pic_colorkey)) == (__pic_alpha|__pic_colorkey) &&
        !(type & __pic_onlyalpha))
    {
//      L_Printf("alpha fill\n");

        scan = pixels;
        for(y = 0; y < size[1]; y++) {
            static const int offset[8][2] = {
                { -1, -1 }, { 0, -1 }, { 1, -1  },
                { -1, 0 },             { 1, 0 },
                { -1, 1 },  { 0, 1 },  { 1, 1 }
            };
            byte basemask, mask, bits;

            if (!y)
                basemask = 0xf8;
            else if (y+1 >= size[1])
                basemask = 0x1f;
            else
                basemask = 0xff;

            for(x = 0; x < size[0]; x++, scan += 4) {
                int color[3];
                byte *src;
                byte c;

                if (scan[3])
                    continue; // not a transparent pixel

                if (!x)
                    mask = basemask & 0xD6;
                else if (x+1 >= size[0])
                    mask = basemask & 0x6B;
                else
                    mask = basemask;

                color[0] = color[1] = color[2] = 0;
                c = 0;
                for(i = 0, bits = 1; i < 8; i++, bits <<= 1) {
                    int dx, dy;

                    if (!(mask & bits))
                        continue;

                    dx = x+offset[i][0];
                    dy = y+offset[i][1];
                    if (dx < 0 || dx >= size[0] || dy < 0 || dy >= size[1])
                        throw LError(" %i %i (%i %i)\n", dx, dy, size[0], size[1]);
                    src = &pixels[4 * (dy * size[0] + dx)];
                    if (!src[3])
                        continue;

                    color[0] += src[0];
                    color[1] += src[1];
                    color[2] += src[2];
                    c++;
                }

                if (c) {
                    scan[0] = color[0] / c;
                    scan[1] = color[1] / c;
                    scan[2] = color[2] / c;
                }
            }
        }
    }

    if (!(type & __pic_alpha))
        type &= ~__pic_maskonly;

    *ptype = type;

    return pixels;
}