view src/video/windows/SDL_gapirender.c @ 5141:da10636e5eca

Making the API simpler, scaling is always defined as linear interpolation and should be supported as much as possible on all renderers.
author Sam Lantinga <slouken@libsdl.org>
date Mon, 31 Jan 2011 22:44:43 -0800
parents 327f181542f1
children
line wrap: on
line source

/***************************************************************************
 *   Copyright (C) 2010 by Andrey Afletdinov <afletdinov@gmail.com>        *
 *                                                                         *
 *   WinCE RAW/GAPI video driver                                           *
 *                                                                         *
 *   Part of the SDL - (Simple DirectMedia Layer)                          *
 *   http://www.libsdl.org                                                 *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "SDL_config.h"

#if SDL_VIDEO_RENDER_GAPI

#include "SDL_windowsvideo.h"
#include "SDL_windowswindow.h"
#include "../SDL_yuv_sw_c.h"

// RawFrameBufferInfo
typedef struct
{
   WORD wFormat;
   WORD wBPP;
   VOID *pFramePointer;
   int  cxStride;
   int  cyStride;
   int  cxPixels;
   int  cyPixels;
} RawFrameBufferInfo;

// GXDeviceInfo
typedef struct
{
    long Version;
    void* pvFrameBuffer;
    unsigned long cbStride;
    unsigned long cxWidth;
    unsigned long cyHeight;
    unsigned long cBPP;
    unsigned long ffFormat;
    char unknown[0x84 - 7 * 4];
} GXDeviceInfo;

// wince: GXDisplayProperties
struct GXDisplayProperties
{
    DWORD cxWidth;
    DWORD cyHeight;
    long cbxPitch;
    long cbyPitch;
    long cBPP;
    DWORD ffFormat;
};

// gx.dll
typedef int   (*PFNGXOpenDisplay)(HWND hWnd, DWORD dwFlags);
typedef int   (*PFNGXCloseDisplay)();
typedef void* (*PFNGXBeginDraw)();
typedef int   (*PFNGXEndDraw)();
typedef struct GXDisplayProperties (*PFNGXGetDisplayProperties)();
typedef int   (*PFNGXSuspend)();
typedef int   (*PFNGXResume)();

typedef struct
{
    // gx.dll
    void*                     hGapiLib;
    PFNGXOpenDisplay          GXOpenDisplay;
    PFNGXCloseDisplay         GXCloseDisplay;
    PFNGXBeginDraw            GXBeginDraw;
    PFNGXEndDraw              GXEndDraw;
    PFNGXGetDisplayProperties GXGetDisplayProperties;
    PFNGXSuspend              GXSuspend;
    PFNGXResume               GXResume;
} GapiInfo;

//#ifndef DM_DISPLAYORIENTATION
//#define DM_DISPLAYORIENTATION 0x00800000L
//#endif

#define FORMAT_565                1
#define FORMAT_555                2
#define FORMAT_OTHER                3

#define GETRAWFRAMEBUFFER        0x00020001
#define GETGXINFO                0x00020000

#define kfPalette                0x10
#define kfDirect                0x20
#define kfDirect555                0x40
#define kfDirect565                0x80

#define GX_FULLSCREEN                0x01

enum ScreenOrientation { ORIENTATION_UNKNOWN = -1, ORIENTATION_UP = DMDO_0, ORIENTATION_DOWN = DMDO_180, ORIENTATION_LEFT = DMDO_270, ORIENTATION_RIGHT = DMDO_90 };
enum ScreenGeometry { GEOMETRY_UNKNOWN, GEOMETRY_PORTRAIT, GEOMETRY_LANDSCAPE, GEOMETRY_SQUARE };
enum FrameBufferFlags { FB_SKIP_OFFSET = 0x0001, FB_RAW_MODE = 0x0002, FB_SUSPENDED = 0x0004 };

// private framebuffer info
typedef struct
{
    int width;
    int height;
    int xpitch;
    int ypitch;
    int offset;
} FrameBufferInfo;

// private display data
typedef struct
{
    unsigned char* pixels;        // video memory
    int format;                        // video format
    FrameBufferInfo fb;                // framebuffer geometry
    GapiInfo* gapi;                // GAPI module
    int userOrientation;
    int systemOrientation;
    int hardwareGeometry;
    int flags;                        // fb flags
    float scale;                // scale pointer position
    int debug;

} WINCE_RenderData;

typedef struct
{
    SDL_SW_YUVTexture *yuv;
    Uint32 format;
    void *pixels;
    int pitch;

} WINCE_TextureData;


// system func
SDL_Renderer*        WINCE_CreateRenderer(SDL_Window* window, Uint32 flags);
void                WINCE_DestroyRenderer(SDL_Renderer* renderer);

int                WINCE_CreateTexture(SDL_Renderer* renderer, SDL_Texture* texture);
void                WINCE_DestroyTexture(SDL_Renderer* renderer, SDL_Texture* texture);
int                WINCE_QueryTexturePixels(SDL_Renderer* renderer, SDL_Texture* texture, void** pixels, int* pitch);
int                WINCE_UpdateTexture(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* rect, const void* pixels, int pitch);
int                WINCE_LockTexture(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* rect, int dirty, void** pixels, int* pitch);
void                WINCE_UnlockTexture(SDL_Renderer* renderer, SDL_Texture* texture);

int                WINCE_Available(void);
void                WINCE_SetupOrientation(WINCE_RenderData* data, int width, int height);

int                WINCE_RenderCopy(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* srect, const SDL_Rect* drect);
void                WINCE_ShowWindow(_THIS, SDL_Window* window, int visible);

void                WINCE_RenderPresent(SDL_Renderer* renderer);
int                WINCE_RenderDrawPoints(SDL_Renderer* renderer, const SDL_Point* points, int count);
int                WINCE_RenderDrawLines(SDL_Renderer* renderer, const SDL_Point* points, int count);
int                WINCE_RenderDrawRects(SDL_Renderer* renderer, const SDL_Rect ** rects, int count);
int                WINCE_RenderFillRects(SDL_Renderer* renderer, const SDL_Rect** rects, int count);

void                WINCE_PointerCoordinateTransform(SDL_Window* window, POINT* pt);
void                WINCE_DumpVideoInfo(WINCE_RenderData* data);
void                WINCE_PortraitTransform(WINCE_RenderData* data, int width, int height);
void                WINCE_LandscapeTransform(WINCE_RenderData* data, int width, int height);
void                WINCE_SquareTransform(WINCE_RenderData* data, int width, int height);
int                WINCE_FixedGeometry(FrameBufferInfo* fb, int bpp, int debug);
int                WINCE_GetDMOrientation(void);
int                WINCE_SetDMOrientation(int orientation);
void                WINCE_UpdateYUVTextureData(SDL_Texture* texture);

// gapi engine specific
int                GAPI_Init(WINCE_RenderData* data, HWND hwnd);
void                GAPI_Quit(WINCE_RenderData* data);

// raw engine specific
int                RAW_Init(WINCE_RenderData* data);
void                RAW_Quit(WINCE_RenderData* data);

// tools
void                FrameBufferRotate(FrameBufferInfo* src, int orientation);
int                GetFrameBufferOrientation(const FrameBufferInfo* src);
void                PointerRotate(POINT* pt, const FrameBufferInfo* fb, int orientation);
void                FrameBufferInitialize(FrameBufferInfo* fb);
void                FrameBufferDumpInfo(const FrameBufferInfo* fb, const char*);
const                char* GetOrientationName(int orientation);
void                UpdateLine16to16(const FrameBufferInfo* fb, const Uint16* src, Uint16* dst, Uint16 width);

// stdlib
static __inline__ int        __abs(int x){ return x < 0 ? -x : x; };
static __inline__ void        __swap(int* a, int* b){ int t = *a; *a = *b; *b = t; };

#define GAPI_RENDER_NAME        "gapi"
#define RAW_RENDER_NAME                "raw"
//
SDL_RenderDriver GAPI_RenderDriver = {
    WINCE_CreateRenderer,
    {
        GAPI_RENDER_NAME,
        (SDL_RENDERER_SINGLEBUFFER | SDL_RENDERER_PRESENTDISCARD),
        (SDL_TEXTUREMODULATE_NONE),
        (SDL_BLENDMODE_NONE),
        7,
        {
            SDL_PIXELFORMAT_RGB555,
            SDL_PIXELFORMAT_RGB565,
            SDL_PIXELFORMAT_YV12,
            SDL_PIXELFORMAT_IYUV,
            SDL_PIXELFORMAT_YUY2,
            SDL_PIXELFORMAT_UYVY,
            SDL_PIXELFORMAT_YVYU
        },
        0,
        0
    }
};

SDL_RenderDriver RAW_RenderDriver = {
    WINCE_CreateRenderer,
    {
        RAW_RENDER_NAME,
        (SDL_RENDERER_SINGLEBUFFER | SDL_RENDERER_PRESENTDISCARD),
        (SDL_TEXTUREMODULATE_NONE),
        (SDL_BLENDMODE_NONE),
        7,
        {
            SDL_PIXELFORMAT_RGB555,
            SDL_PIXELFORMAT_RGB565,
            SDL_PIXELFORMAT_YV12,
            SDL_PIXELFORMAT_IYUV,
            SDL_PIXELFORMAT_YUY2,
            SDL_PIXELFORMAT_UYVY,
            SDL_PIXELFORMAT_YVYU
        },
        0,
        0
    }
};

int WINCE_Available(void)
{
    void* render_gapi;
    const char* preferably = SDL_getenv("SDL_VIDEO_RENDERER");

    // raw check
    RawFrameBufferInfo rfbi = { 0 };
    HDC hdc = GetDC(NULL);
    int render_raw = ExtEscape(hdc, GETRAWFRAMEBUFFER, 0, NULL, sizeof(RawFrameBufferInfo), (char *) &rfbi);
    ReleaseDC(NULL, hdc);

    if(render_raw != 0 && rfbi.cxPixels != 0 && rfbi.cyPixels != 0 &&
       rfbi.pFramePointer != 0 && rfbi.cxStride != 0 && rfbi.cyStride != 0)
        render_raw = 1;

    if(preferably && 0 == SDL_strcasecmp(preferably, RAW_RENDER_NAME)) return 0 != render_raw;

    // gapi check
    render_gapi = SDL_LoadObject("\\Windows\\gx.dll");
    if(0 == render_gapi)
        render_gapi = SDL_LoadObject("gx.dll");
    SDL_UnloadObject(render_gapi);

    if(preferably && 0 == SDL_strcasecmp(preferably, GAPI_RENDER_NAME)) return 0 != render_gapi;

    return 0 != render_raw || 0 != render_gapi;
}

void WINCE_AddRenderDriver(_THIS)
{
    HDC hdc;
    void* render_gapi;
    int render_raw, ii;
    const char* preferably = SDL_getenv("SDL_VIDEO_RENDERER");

   // raw check
    RawFrameBufferInfo rfbi = { 0 };
    hdc = GetDC(NULL);
    render_raw = ExtEscape(hdc, GETRAWFRAMEBUFFER, 0, NULL, sizeof(RawFrameBufferInfo), (char *) &rfbi);
    ReleaseDC(NULL, hdc);

    if(render_raw != 0 && rfbi.cxPixels != 0 && rfbi.cyPixels != 0 &&
            rfbi.pFramePointer != 0 && rfbi.cxStride != 0 && rfbi.cyStride != 0)
            render_raw = 1;

    // gapi check
    render_gapi = SDL_LoadObject("\\Windows\\gx.dll");
    if(0 == render_gapi)
        render_gapi = SDL_LoadObject("gx.dll");

    if(render_gapi)
        SDL_UnloadObject(render_gapi);

    for(ii = 0; ii < _this->num_displays; ++ii)
    {
        if(preferably)
        {
            if(0 == SDL_strcasecmp(preferably, RAW_RENDER_NAME) && render_raw)
                SDL_AddRenderDriver(&_this->displays[ii], &RAW_RenderDriver);
            else
            if(0 == SDL_strcasecmp(preferably, GAPI_RENDER_NAME) && render_gapi)
                SDL_AddRenderDriver(&_this->displays[ii], &GAPI_RenderDriver);
        }
        else
        {
            if(render_raw)
                SDL_AddRenderDriver(&_this->displays[ii], &RAW_RenderDriver);
            if(render_gapi)
                SDL_AddRenderDriver(&_this->displays[ii], &GAPI_RenderDriver);
        }
    }
}

SDL_Renderer* WINCE_CreateRenderer(SDL_Window* window, Uint32 flags)
{
    SDL_VideoDisplay* display = window->display;
    SDL_DisplayMode* displayMode = &display->current_mode;
    SDL_WindowData* windowdata = (SDL_WindowData *) window->driverdata;
    SDL_Renderer* renderer;
    WINCE_RenderData* data;
    int bpp;
    Uint32 Rmask, Gmask, Bmask, Amask;

    if(!(window->flags & SDL_WINDOW_FULLSCREEN))
        window->flags |= SDL_WINDOW_FULLSCREEN;

    if(!SDL_PixelFormatEnumToMasks(displayMode->format, &bpp, &Rmask, &Gmask, &Bmask, &Amask))
    {
        SDL_SetError("Unknown display format");
        return NULL;
    }

    switch(window->fullscreen_mode.format)
    {
        case SDL_PIXELFORMAT_RGB555:
        case SDL_PIXELFORMAT_RGB565:
            break;

        default:
            SDL_SetError("Support only 16 or 15 bpp");
            return NULL;
    }

    renderer = (SDL_Renderer*) SDL_calloc(1, sizeof(SDL_Renderer));
    if(!renderer)
    {
        SDL_OutOfMemory();
        return NULL;
    }

    data = (WINCE_RenderData*) SDL_calloc(1, sizeof(WINCE_RenderData));
    if(!data)
    {
        WINCE_DestroyRenderer(renderer);
        SDL_OutOfMemory();
        return NULL;
    }

    // initialize internal engine
    if(!RAW_Init(data) && !GAPI_Init(data, windowdata->hwnd))
    {
        WINCE_DestroyRenderer(renderer);
        return NULL;
    }


    // set debug
    data->debug        = SDL_getenv("DEBUG_VIDEO_GAPI") || SDL_getenv("GAPI_RENDERER_DEBUG") ? 1 : 0;
#if defined(DEBUG_VIDEO_GAPI) || defined(GAPI_RENDERER_DEBUG)
    data->debug        = 1;
#endif

    windowdata->videodata->render = data->gapi ? RENDER_GAPI : RENDER_RAW;
    windowdata->videodata->CoordTransform = WINCE_PointerCoordinateTransform;

    window->display->device->MaximizeWindow = NULL;
    window->display->device->MinimizeWindow = NULL;

    WINCE_SetupOrientation(data, window->w, window->h);

    renderer->CreateTexture = WINCE_CreateTexture;
    renderer->DestroyTexture = WINCE_DestroyTexture;
    renderer->QueryTexturePixels = WINCE_QueryTexturePixels;
    renderer->UpdateTexture = WINCE_UpdateTexture;
    renderer->LockTexture = WINCE_LockTexture;
    renderer->UnlockTexture = WINCE_UnlockTexture;

    renderer->RenderCopy = WINCE_RenderCopy;
    renderer->DestroyRenderer = WINCE_DestroyRenderer;

    renderer->RenderPresent = WINCE_RenderPresent;
    renderer->RenderDrawPoints = WINCE_RenderDrawPoints;
    renderer->RenderDrawLines = WINCE_RenderDrawLines;
    renderer->RenderDrawRects = WINCE_RenderDrawRects;
    renderer->RenderFillRects = WINCE_RenderFillRects;

    renderer->info = data->gapi ? GAPI_RenderDriver.info : RAW_RenderDriver.info;

    renderer->window = window;
    renderer->driverdata = data;

    return renderer;
}

void WINCE_DestroyRenderer(SDL_Renderer* renderer)
{
    WINCE_RenderData *renderdata = (WINCE_RenderData*) renderer->driverdata;

    if(renderdata)
    {
        if(renderdata->gapi)
            GAPI_Quit(renderdata);
        else
            RAW_Quit(renderdata);

        SDL_free(renderdata);
    }

    SDL_free(renderer);
}

int WINCE_CreateTexture(SDL_Renderer* renderer, SDL_Texture* texture)
{
    WINCE_TextureData* texturedata = (WINCE_TextureData*) SDL_calloc(1, sizeof(WINCE_TextureData));
    if(NULL == texturedata)
    {
        SDL_OutOfMemory();
        return -1;
    }

    texturedata->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
    texturedata->pixels = SDL_malloc(texture->h * texturedata->pitch);
    if(NULL == texturedata->pixels)
    {
        SDL_OutOfMemory();
        return -1;
    }

    if(SDL_ISPIXELFORMAT_FOURCC(texture->format))
    {
        SDL_Window* window = renderer->window;
        SDL_VideoDisplay* display = window->display;

        texturedata->yuv = SDL_SW_CreateYUVTexture(texture->format, texture->w, texture->h);
        if(NULL == texturedata->yuv)
        {
            SDL_OutOfMemory();
            return -1;
        }
        texturedata->format = display->current_mode.format;
    }
    else
    {
        texturedata->yuv = NULL;
        texturedata->format = texture->format;
    }

    texture->driverdata = texturedata;

    return 0;
}

void WINCE_DestroyTexture(SDL_Renderer* renderer, SDL_Texture* texture)
{
    WINCE_TextureData *texturedata = (WINCE_TextureData*) texture->driverdata;

    if(texturedata)
    {
        if(texturedata->yuv) SDL_SW_DestroyYUVTexture(texturedata->yuv);
        if(texturedata->pixels) SDL_free(texturedata->pixels);
        SDL_free(texturedata);
        texture->driverdata = NULL;
    }
}

int WINCE_QueryTexturePixels(SDL_Renderer* renderer, SDL_Texture* texture, void** pixels, int* pitch)
{
    WINCE_TextureData* texturedata = (WINCE_TextureData*) texture->driverdata;

    if(texturedata->yuv)
        return SDL_SW_QueryYUVTexturePixels(texturedata->yuv, pixels, pitch);

    *pixels = texturedata->pixels;
    *pitch = texturedata->pitch;

    return 0;
}

int WINCE_UpdateTexture(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* rect, const void* pixels, int pitch)
{
    WINCE_TextureData* texturedata = (WINCE_TextureData*) texture->driverdata;

    if(texturedata->yuv)
    {
        if(SDL_SW_UpdateYUVTexture(texturedata->yuv, rect, pixels, pitch) < 0)
            return -1;
        WINCE_UpdateYUVTextureData(texture);
        return 0;
    }

    if(0 < rect->w && 0 < rect->h)
    {
        const unsigned char *src = ((const unsigned char*) pixels);
        unsigned char *dst = ((unsigned char*) texturedata->pixels) +
                                rect->y * texturedata->pitch +
                                rect->x * SDL_BYTESPERPIXEL(texture->format);
        int length = rect->w * SDL_BYTESPERPIXEL(texture->format);
        int height = rect->h;

        while(height--)
        {
            SDL_memcpy(dst, src, length);
            dst += texturedata->pitch;
            src += pitch;
        }
    }

    return 0;
}

int WINCE_LockTexture(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* rect, int dirty, void** pixels, int* pitch)
{
    WINCE_TextureData *texturedata = (WINCE_TextureData*) texture->driverdata;

    if(texturedata->yuv)
        return SDL_SW_LockYUVTexture(texturedata->yuv, rect, dirty, pixels, pitch);

    *pixels = (void *) ((unsigned char*) texturedata->pixels +
                    rect->y * texturedata->pitch +
                    rect->x * SDL_BYTESPERPIXEL(texture->format));
    *pitch = texturedata->pitch;
    return 0;
}

void WINCE_UnlockTexture(SDL_Renderer* renderer, SDL_Texture* texture)
{
    WINCE_TextureData *texturedata = (WINCE_TextureData*) texture->driverdata;

    if(texturedata->yuv)
    {
        SDL_SW_UnlockYUVTexture(texturedata->yuv);
        WINCE_UpdateYUVTextureData(texture);
    }
}

int WINCE_RenderCopy(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* srect, const SDL_Rect* drect)
{
    WINCE_RenderData* dstdata = (WINCE_RenderData*) renderer->driverdata;
    WINCE_TextureData* srcdata = (WINCE_TextureData*) texture->driverdata;
    const unsigned char *src;
    unsigned char *dst;

    if((dstdata->flags & FB_SUSPENDED) ||
       0 >= srect->w || 0 >= srect->h) return 0;

    // lock gapi
    if(dstdata->gapi) dstdata->gapi->GXBeginDraw();

    src = ((const unsigned char*) srcdata->pixels);
    dst = dstdata->pixels + (dstdata->flags & FB_SKIP_OFFSET ? 0 : dstdata->fb.offset) +
                                drect->y * dstdata->fb.ypitch +
                                drect->x * dstdata->fb.xpitch;
    if(srcdata->yuv)
    {
        return SDL_SW_CopyYUVToRGB(srcdata->yuv,
                                   srect, srcdata->format,
                                   drect->w, drect->h, dst,
                                   dstdata->fb.ypitch);
    }
    else
    {
        int height = drect->h;
        int length = drect->w * SDL_BYTESPERPIXEL(texture->format); // in bytes

        while(height--)
        {
            switch(SDL_BYTESPERPIXEL(texture->format))
            {
                case 2: UpdateLine16to16(&dstdata->fb, (Uint16*) src, (Uint16*) dst, length >> 1); break;

                default: break;
            }

            dst += dstdata->fb.ypitch;
            src += srcdata->pitch;
        }
    }

    // unlock gapi
    if(dstdata->gapi) dstdata->gapi->GXEndDraw();

    return 0;
}

void WINCE_RenderPresent(SDL_Renderer* renderer)
{
}

int WINCE_RenderDrawPoints(SDL_Renderer* renderer, const SDL_Point* points, int count)
{
    SDL_Unsupported();
    return -1;
}

int WINCE_RenderDrawLines(SDL_Renderer* renderer, const SDL_Point* points, int count)
{
    SDL_Unsupported();
    return -1;
}

int WINCE_RenderDrawRects(SDL_Renderer* renderer, const SDL_Rect ** rects, int count)
{
    SDL_Unsupported();
    return -1;
}

int WINCE_RenderFillRects(SDL_Renderer* renderer, const SDL_Rect** rects, int count)
{
    SDL_Unsupported();
    return -1;
}



void WINCE_SetupOrientation(WINCE_RenderData* data, int width, int height)
{
    const float maxW1 = (float)(GetSystemMetrics(SM_CXSCREEN) > GetSystemMetrics(SM_CYSCREEN) ? GetSystemMetrics(SM_CXSCREEN) : GetSystemMetrics(SM_CYSCREEN));
    const float maxW2 = (float)(data->fb.width > data->fb.height ? data->fb.width : data->fb.height);

    // scale define
    data->scale = maxW2 / maxW1;

    // init fb values
    FrameBufferInitialize(&data->fb);

    // orientation values
    data->userOrientation = ORIENTATION_UP;
    data->systemOrientation = WINCE_GetDMOrientation();
    data->hardwareGeometry = data->fb.width == data->fb.height ? GEOMETRY_SQUARE :
                                (data->fb.width < data->fb.height ? GEOMETRY_PORTRAIT : GEOMETRY_LANDSCAPE);

    if(data->debug)
        WINCE_DumpVideoInfo(data);

    if(data->systemOrientation == ORIENTATION_UNKNOWN)
        data->systemOrientation = ORIENTATION_UP;

    data->userOrientation = ORIENTATION_UP;

    switch(data->hardwareGeometry)
    {
        case GEOMETRY_PORTRAIT:  WINCE_PortraitTransform(data, width, height); break;
        case GEOMETRY_LANDSCAPE: WINCE_LandscapeTransform(data, width, height); break;
        case GEOMETRY_SQUARE:    WINCE_SquareTransform(data, width, height); break;
        default: break;
    }

    // debug
    if(data->debug)
    {
        printf("\n");
        printf("user video width:          %d\n", width);
        printf("user video height:         %d\n", height);
        FrameBufferDumpInfo(&data->fb, "user");
    }
}

void WINCE_DumpVideoInfo(WINCE_RenderData* data)
{
    // get oem info
    WCHAR oemInfo[48];
    SDL_memset(oemInfo, 0, sizeof(oemInfo));
    SystemParametersInfo(SPI_GETOEMINFO, sizeof(oemInfo) - sizeof(WCHAR), oemInfo, 0);

    printf("hardware oem: ");
    wprintf(oemInfo);
    printf("\n");

    printf("video driver mode:             %s\n", (data->flags & FB_RAW_MODE ? RAW_RENDER_NAME : GAPI_RENDER_NAME));
    printf("GetSystemMetrics(SM_CXSCREEN): %d\n", GetSystemMetrics(SM_CXSCREEN));
    printf("GetSystemMetrics(SM_CYSCREEN): %d\n", GetSystemMetrics(SM_CYSCREEN));
    printf("scale coord:                   %f\n", data->scale);

    FrameBufferDumpInfo(&data->fb, "hardware");

    printf("display format:                %p\n", data->format);
    printf("display bits per pixel:        %d\n", SDL_BITSPERPIXEL(data->format));
    printf("display bytes per pixel:       %d\n", SDL_BYTESPERPIXEL(data->format));
    printf("display memory:                %p\n", data->pixels);
    printf("system orientation:            %d, %s\n", data->systemOrientation, GetOrientationName(data->systemOrientation));
    printf("hardware geometry:             %d\n", data->hardwareGeometry);
}

void WINCE_ShowWindow(_THIS, SDL_Window* window, int visible)
{
    SDL_WindowData* windowdata = (SDL_WindowData*) window->driverdata;
    SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
    SDL_Renderer* renderer = (SDL_Renderer*) window->renderer;

    if(visible)
    {
        if(window->flags & SDL_WINDOW_FULLSCREEN)
        {
            if(videodata->SHFullScreen)
                videodata->SHFullScreen(windowdata->hwnd, SHFS_HIDETASKBAR | SHFS_HIDESTARTICON | SHFS_HIDESIPBUTTON);
            ShowWindow(FindWindow(TEXT("HHTaskBar"), NULL), SW_HIDE);
        }

        ShowWindow(windowdata->hwnd, SW_SHOW);
        SetForegroundWindow(windowdata->hwnd);

        if(renderer &&
            (videodata->render == RENDER_GAPI || videodata->render == RENDER_RAW))
        {
            WINCE_RenderData* renderdata = (WINCE_RenderData*) renderer->driverdata;
            renderdata->flags &= ~FB_SUSPENDED;
            if(renderdata->gapi) renderdata->gapi->GXResume();
        }
    }
    else
    {
        if(renderer &&
            (videodata->render == RENDER_GAPI || videodata->render == RENDER_RAW))
        {
            WINCE_RenderData* renderdata = (WINCE_RenderData*) renderer->driverdata;
            if(renderdata->gapi) renderdata->gapi->GXSuspend();
            renderdata->flags |= FB_SUSPENDED;
        }

        ShowWindow(windowdata->hwnd, SW_HIDE);

        if(window->flags & SDL_WINDOW_FULLSCREEN)
        {
            if(videodata->SHFullScreen)
                videodata->SHFullScreen(windowdata->hwnd, SHFS_SHOWTASKBAR | SHFS_SHOWSTARTICON | SHFS_SHOWSIPBUTTON);
            ShowWindow(FindWindow(TEXT("HHTaskBar"), NULL), SW_SHOW);
        }
    }
}


void WINCE_PointerCoordinateTransform(SDL_Window* window, POINT* pt)
{
    WINCE_RenderData* data = (WINCE_RenderData*) window->renderer->driverdata;

    pt->x = (LONG)(pt->x * data->scale);
    pt->y = (LONG)(pt->y * data->scale);

    PointerRotate(pt, &data->fb, data->userOrientation);
}

void WINCE_PortraitTransform(WINCE_RenderData* data, int width, int height)
{
    if(data->systemOrientation != ORIENTATION_UP)
        FrameBufferRotate(&data->fb, data->systemOrientation);

    if(data->fb.width != width || data->fb.height != height) {
        switch(data->systemOrientation)
        {
            case ORIENTATION_UP:
            case ORIENTATION_LEFT: data->userOrientation = ORIENTATION_RIGHT; break;
            case ORIENTATION_RIGHT:
            case ORIENTATION_DOWN: data->userOrientation = ORIENTATION_LEFT; break;
            default: break;
        }
    }

    if(data->userOrientation != ORIENTATION_UP)
        FrameBufferRotate(&data->fb, data->userOrientation);
}

void WINCE_LandscapeTransform(WINCE_RenderData* data, int width, int height)
{
    switch(data->systemOrientation)
    {
        case ORIENTATION_UP:  FrameBufferRotate(&data->fb, ORIENTATION_LEFT); break;
        case ORIENTATION_LEFT:FrameBufferRotate(&data->fb, ORIENTATION_DOWN); break;
        case ORIENTATION_DOWN:FrameBufferRotate(&data->fb, ORIENTATION_RIGHT); break;
        default: break;
    }

    if(data->fb.width != width || data->fb.height != height)
    switch(data->systemOrientation)
    {
        case ORIENTATION_UP:
        case ORIENTATION_LEFT: data->userOrientation = ORIENTATION_RIGHT; break;
        case ORIENTATION_RIGHT:
        case ORIENTATION_DOWN: data->userOrientation = ORIENTATION_LEFT; break;
        default: break;
    }

    if(data->userOrientation != ORIENTATION_UP)
        FrameBufferRotate(&data->fb, data->userOrientation);
}

void WINCE_SquareTransform(WINCE_RenderData* data, int width, int height)
{
    WINCE_PortraitTransform(data, width, height);
}

int WINCE_FixedGeometry(FrameBufferInfo* fb, int bpp, int debug)
{
    // check square
    if(GetSystemMetrics(SM_CXSCREEN) == GetSystemMetrics(SM_CYSCREEN) &&
        fb->width != fb->height)
    {
        if(fb->width < fb->height)
            fb->height = fb->width;
        else
        if(fb->height < fb->width)
            fb->width = fb->height;
    }

    // check width
    if(__abs(fb->xpitch) == bpp &&
        fb->width  != __abs(fb->ypitch) / bpp)
    {
        if(fb->height == __abs(fb->ypitch) / bpp)
            {
            __swap(&fb->width, &fb->height);

            if(debug)
                printf("WINCE_FixedGeometry: width: %d, height: %d\n", fb->width, fb->height);
        }
        else
            return -1;
    }
    else
    // check height
    if(__abs(fb->ypitch) == bpp &&
        fb->height != __abs(fb->xpitch) / bpp)
    {
        if(fb->width  == __abs(fb->xpitch) / bpp)
            {
            __swap(&fb->width, &fb->height);

            if(debug)
                printf("WINCE_FixedGeometry: width: %d, height: %d\n", fb->width, fb->height);
        }
        else
            return -1;
    }

    return 0;
}

void WINCE_UpdateYUVTextureData(SDL_Texture* texture)
{
    WINCE_TextureData* texturedata = (WINCE_TextureData*) texture->driverdata;
    SDL_Rect rect;

    rect.x = 0;
    rect.y = 0;
    rect.w = texture->w;
    rect.h = texture->h;
    SDL_SW_CopyYUVToRGB(texturedata->yuv, &rect, texturedata->format, texture->w, texture->h, texturedata->pixels, texturedata->pitch);
}

int GAPI_Init(WINCE_RenderData* data, HWND hwnd)
{
    if(NULL == data->gapi)
    {
        struct GXDisplayProperties gxProperties;
        GXDeviceInfo gxInfo = { 0 };
        HDC hdc;
        int enable, result;
        const char* preferably = SDL_getenv("SDL_VIDEO_RENDERER");
        if(preferably && 0 != SDL_strcasecmp(preferably, GAPI_RENDER_NAME)) return 0;

        data->gapi = (GapiInfo *) SDL_calloc(1, sizeof(GapiInfo));
        if(NULL == data->gapi)
        {
            SDL_OutOfMemory();
            return 0;
        }

        data->gapi->hGapiLib = SDL_LoadObject("\\Windows\\gx.dll");
        if(0 == data->gapi->hGapiLib)
        {
            data->gapi->hGapiLib = SDL_LoadObject("gx.dll");
            if(0 == data->gapi->hGapiLib) return 0;
        }

        // load gapi library
#define LINK(type,name,import) name=(PFN##type)SDL_LoadFunction(data->gapi->hGapiLib,import)
        LINK(GXOpenDisplay,         data->gapi->GXOpenDisplay,         "?GXOpenDisplay@@YAHPAUHWND__@@K@Z");
        LINK(GXCloseDisplay,        data->gapi->GXCloseDisplay,        "?GXCloseDisplay@@YAHXZ");
        LINK(GXBeginDraw,           data->gapi->GXBeginDraw,           "?GXBeginDraw@@YAPAXXZ");
        LINK(GXEndDraw,             data->gapi->GXEndDraw,             "?GXEndDraw@@YAHXZ");
        LINK(GXGetDisplayProperties,data->gapi->GXGetDisplayProperties,"?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ");
        LINK(GXSuspend,             data->gapi->GXSuspend,             "?GXSuspend@@YAHXZ");
        LINK(GXResume,              data->gapi->GXResume,              "?GXResume@@YAHXZ");
#undef LINK

        enable = data->gapi->GXGetDisplayProperties && data->gapi->GXCloseDisplay && data->gapi->GXOpenDisplay &&
            data->gapi->GXBeginDraw && data->gapi->GXEndDraw && data->gapi->GXSuspend && data->gapi->GXResume;

        if(!enable)
        {
            SDL_SetError("GAPI_Init: error gx.dll: internal error");
            GAPI_Quit(data);
            return 0;
        }

        if(0 == data->gapi->GXOpenDisplay(hwnd, GX_FULLSCREEN))
        {
            SDL_SetError("GAPI_Init: couldn't initialize GAPI");
            GAPI_Quit(data);
            return 0;
        }

        gxProperties = data->gapi->GXGetDisplayProperties();

        // fill FrameBufferInfo
        data->fb.xpitch = gxProperties.cbxPitch;
        data->fb.ypitch = gxProperties.cbyPitch;
        data->fb.width  = gxProperties.cxWidth;
        data->fb.height = gxProperties.cyHeight;
        data->fb.offset = 0;

        if((gxProperties.ffFormat & kfDirect565) || 16 == gxProperties.cBPP)
            data->format = SDL_PIXELFORMAT_RGB565;
        else
        if((gxProperties.ffFormat & kfDirect555) || 15 == gxProperties.cBPP)
            data->format = SDL_PIXELFORMAT_RGB555;
        else
            data->format = 0;

        // get pixels
        hdc = GetDC(NULL);

        gxInfo.Version = 100;
        result = ExtEscape(hdc, GETGXINFO, 0, NULL, sizeof(gxInfo), (char *) &gxInfo);
        ReleaseDC(NULL, hdc);

        if(result > 0)
        {
            // more debug
            if(data->debug)
            {
                int i;

                printf("GXDeviceInfo.pvFrameBuffer:    %p\n", gxInfo.pvFrameBuffer);
                printf("GXDeviceInfo.cxWidth:          %d\n", gxInfo.cxWidth);
                printf("GXDeviceInfo.cyHeight:         %d\n", gxInfo.cyHeight);
                printf("GXDeviceInfo.cbStride:         %d\n", gxInfo.cbStride);
                printf("GXDeviceInfo.cBPP:             %d\n", gxInfo.cBPP);
                printf("GXDeviceInfo.ffFormat:        0x%x\n", gxInfo.ffFormat);

                printf("GXDeviceInfo.unk:\n");
                for(i = 0; i <  sizeof(gxInfo.unknown); ++i)
                    printf("0x%02hhX,", gxInfo.unknown[i]);
                printf("\n");
            }

                if(gxInfo.ffFormat && gxInfo.ffFormat != gxProperties.ffFormat) {
                    if((gxInfo.ffFormat & kfDirect565) || 16 == gxInfo.cBPP)
                        data->format = SDL_PIXELFORMAT_RGB565;
                    else
                    if((gxInfo.ffFormat & kfDirect555) || 15 == gxInfo.cBPP)
                        data->format = SDL_PIXELFORMAT_RGB555;
                }

                data->pixels = gxInfo.pvFrameBuffer;
        }
        else
        {
            data->flags |= FB_SKIP_OFFSET;
            data->pixels = data->gapi->GXBeginDraw();
            data->gapi->GXEndDraw();

            if(data->debug)
            {
                printf("GAPI_Init\n");
                printf("use GXBeginDraw:               %p\n", data->pixels);
                printf("use skip offset\n");
            }
        }

        if(0 == data->format ||
            0 > WINCE_FixedGeometry(&data->fb, SDL_BYTESPERPIXEL(data->format), data->debug))
        {
            SDL_SetError("GAPI_Init: unknown hardware");
            GAPI_Quit(data);
            return 0;
        }
    }

    return data->gapi && data->pixels ? 1 : 0;
}

void GAPI_Quit(WINCE_RenderData* data)
{
    if(data->gapi)
    {
        if(data->gapi->GXCloseDisplay) data->gapi->GXCloseDisplay(); 
        if(data->gapi->hGapiLib)  SDL_UnloadObject(data->gapi->hGapiLib);

        SDL_free(data->gapi);
        data->gapi = NULL;
    }
}

int RAW_Init(WINCE_RenderData* data)
{
    RawFrameBufferInfo rfbi = { 0 };
    HDC hdc;
    int result;
    const char* preferably = SDL_getenv("SDL_VIDEO_RENDERER");
    if(preferably && 0 != SDL_strcasecmp(preferably, RAW_RENDER_NAME)) return 0;

    hdc = GetDC(NULL);
    result = ExtEscape(hdc, GETRAWFRAMEBUFFER, 0, NULL, sizeof(RawFrameBufferInfo), (char *) &rfbi);
    ReleaseDC(NULL, hdc);

    //disable
    if(result == 0 || rfbi.pFramePointer == 0 ||
            rfbi.cxPixels == 0 || rfbi.cyPixels == 0 ||
        rfbi.cxStride == 0 || rfbi.cyStride == 0) return 0;

    data->flags     = FB_RAW_MODE;

    // fill FrameBufferInfo
    SDL_memset(&data->fb, 0, sizeof(FrameBufferInfo));

    data->fb.xpitch = rfbi.cxStride;
    data->fb.ypitch = rfbi.cyStride;
    data->fb.width  = rfbi.cxPixels;
    data->fb.height = rfbi.cyPixels;
    data->fb.offset = 0;

    if((FORMAT_565 & rfbi.wFormat) || 16 == rfbi.wBPP)
        data->format = SDL_PIXELFORMAT_RGB565;
    else
    if((FORMAT_555 & rfbi.wFormat) || 15 == rfbi.wBPP)
        data->format = SDL_PIXELFORMAT_RGB555;
    else
        data->format = 0;

    if(0 == data->format ||
        0 > WINCE_FixedGeometry(&data->fb, SDL_BYTESPERPIXEL(data->format), data->debug))
    {
        SDL_SetError("RAW_Init: unknown hardware");
        RAW_Quit(data);
        return 0;
    }

    data->pixels = rfbi.pFramePointer;

    return data->pixels ? 1 : 0;
}

void RAW_Quit(WINCE_RenderData* data)
{
}

void FrameBufferInitialize(FrameBufferInfo* fb)
{
    int orientation = GetFrameBufferOrientation(fb);

    // set correct start offset
    switch(orientation)
    {
        case ORIENTATION_UP:
            fb->offset = 0;
            break;

        case ORIENTATION_LEFT:
            fb->offset = __abs(fb->ypitch * (fb->height - 1));
            break;

        case ORIENTATION_RIGHT:
            fb->offset = __abs(fb->xpitch * (fb->width - 1));
            break;

        case ORIENTATION_DOWN:
            fb->offset = __abs(fb->xpitch * (fb->width - 1) +
                                fb->ypitch * (fb->height - 1));
            break;

        default: break;
    }

    //if(orientation != ORIENTATION_UP)
    switch(orientation)
    {
        case ORIENTATION_LEFT: FrameBufferRotate(fb, ORIENTATION_RIGHT); break;
        case ORIENTATION_RIGHT:FrameBufferRotate(fb, ORIENTATION_LEFT); break;
        case ORIENTATION_DOWN: FrameBufferRotate(fb, ORIENTATION_DOWN); break;

        default: break;
    }
}

int GetFrameBufferOrientation(const FrameBufferInfo* src)
{
    if(src->xpitch > 0 && src->ypitch > 0)
        return ORIENTATION_UP;
    else
    if(src->xpitch > 0 && src->ypitch < 0)
        return ORIENTATION_LEFT;
    else
    if(src->xpitch < 0 && src->ypitch > 0)
        return ORIENTATION_RIGHT;
    else
    if(src->xpitch < 0 && src->ypitch < 0)
        return ORIENTATION_DOWN;

    return ORIENTATION_UNKNOWN;
}

void FrameBufferRotate(FrameBufferInfo* dst, int orientation)
{
    FrameBufferInfo src;
    // copy dst -> src
    SDL_memcpy(&src, dst, sizeof(FrameBufferInfo));

    switch(orientation)
    {
        case ORIENTATION_LEFT:
            dst->width  = src.height;
            dst->height = src.width;
            dst->xpitch = src.ypitch;
            dst->ypitch = -src.xpitch;
            dst->offset = src.offset + src.xpitch * (src.width - 1);
            break;

        case ORIENTATION_RIGHT:
            dst->width  = src.height;
            dst->height = src.width;
            dst->xpitch = -src.ypitch;
            dst->ypitch = src.xpitch;
            dst->offset = src.offset + src.ypitch * (src.height - 1);
            break;

        case ORIENTATION_DOWN:
            FrameBufferRotate(dst, ORIENTATION_LEFT);
            FrameBufferRotate(dst, ORIENTATION_LEFT);
            break;

        default:
            break;
    }
}

void PointerRotate(POINT* pt, const FrameBufferInfo* fb, int orientation)
{
    switch(orientation)
    {
        case ORIENTATION_UP:
            break;

        case ORIENTATION_LEFT:
        {
            int temp = pt->y;
            pt->y = fb->height - pt->x;
            pt->x = temp;
        }
            break;

        case ORIENTATION_RIGHT:
        {
            int temp = pt->x;
            pt->x = fb->width - pt->y;
            pt->y = temp;
        }
            break;

        case ORIENTATION_DOWN:
            pt->x = fb->width  - pt->x;
            pt->y = fb->height - pt->y;
            break;

        default: break;
    }
}

const char* GetOrientationName(int orientation)
{
    switch(orientation)
    {
        case ORIENTATION_UP:        return "UP";
        case ORIENTATION_DOWN:      return "DOWN";
        case ORIENTATION_LEFT:      return "LEFT";
        case ORIENTATION_RIGHT:     return "RIGHT";
        default: break;
    }

    return "UNKNOWN";
}

int WINCE_GetDMOrientation(void)
{
    DEVMODE sDevMode = {0};
    sDevMode.dmSize = sizeof(DEVMODE);
    sDevMode.dmFields = DM_DISPLAYORIENTATION;

    // DMDO_0, DMDO_90, DMDO_180, DMDO_270
    if(DISP_CHANGE_BADMODE != ChangeDisplaySettingsEx(NULL, &sDevMode, 0, CDS_TEST, NULL))
        switch(sDevMode.dmDisplayOrientation)
        {
            case DMDO_0:        return DMDO_0;
            case DMDO_90:        return DMDO_90;
            case DMDO_180:        return DMDO_180;
            case DMDO_270:        return DMDO_270;
            default: break;
        }

    SDL_SetError("WINCE_GetDMOrientation: ChangeDisplaySettingsEx return BADMODE");
    return -1;
}

int WINCE_SetDMOrientation(int orientation)
{
    DEVMODE sDevMode = {0};
    sDevMode.dmSize = sizeof(DEVMODE);
    sDevMode.dmFields = DM_DISPLAYORIENTATION;

    switch(orientation)
    {
        case DMDO_0:        sDevMode.dmDisplayOrientation = DMDO_0;   break;
        case DMDO_90:        sDevMode.dmDisplayOrientation = DMDO_90;  break;
        case DMDO_180:        sDevMode.dmDisplayOrientation = DMDO_180; break;
        case DMDO_270:        sDevMode.dmDisplayOrientation = DMDO_270; break;
        default: return 0;
    }

    if(DISP_CHANGE_BADMODE != ChangeDisplaySettingsEx(NULL, &sDevMode, 0, CDS_RESET, NULL))
        return 1;

    SDL_SetError("WINCE_SetDMOrientation: ChangeDisplaySettingsEx return BADMODE");
    return -1;
}

void FrameBufferDumpInfo(const FrameBufferInfo* fb, const char* name)
{
    int orientation;

    printf("%s fb.width:       %d\n", name, fb->width);
    printf("%s fb.height:      %d\n", name, fb->height);
    printf("%s fb.xpitch:      %d\n", name, fb->xpitch);
    printf("%s fb.ypitch:      %d\n", name, fb->ypitch);
    printf("%s fb.offset:      %d\n", name, fb->offset);

    orientation = GetFrameBufferOrientation(fb);
    printf("%s fb.orientation: %d, %s\n", name, orientation, GetOrientationName(orientation));
}

void UpdateLine16to16(const FrameBufferInfo* fb, const Uint16* src, Uint16* dst, Uint16 width)
{
    if(2 == fb->xpitch)
    {
        switch(width)
        {
            case 1:
                *dst = *src;
                break;

            case 2:
                *((Uint32*) dst) = *((Uint32*) src);
                break;

            default:
                SDL_memcpy(dst, src, width * 2);
                break;
        }
    }
    else
    if(-2 == fb->xpitch)
    {
        while(width--)
            *dst-- = *src++;
    }
    else
    {
        while(width--)
        {
            *dst = *src++;
            dst += fb->xpitch / 2;
        }
    }
}

#endif // SDL_VIDEO_RENDER_GAPI