New low level driver interface: Only Win32 ported currently Significant reduction in GDISP stack usage Improved performance particularly for native streaming drivers New circle, ellipse, arc routines (draw and fill) that are significantly more efficient and don't overdraw New arc draw algorithm that measures angles correctly. New arc fill algorithm for that actually works without overdrawing or gaps. Much more to come...
978 lines
26 KiB
C
978 lines
26 KiB
C
/*
|
|
* This file is subject to the terms of the GFX License. If a copy of
|
|
* the license was not distributed with this file, you can obtain one at:
|
|
*
|
|
* http://ugfx.org/license.html
|
|
*/
|
|
|
|
/**
|
|
* @file drivers/multiple/Win32/gdisp_lld.c
|
|
* @brief GDISP Graphics Driver subsystem low level driver source for Win32.
|
|
*
|
|
* @addtogroup GDISP
|
|
* @{
|
|
*/
|
|
|
|
#include "gfx.h"
|
|
|
|
#if GFX_USE_GDISP /*|| defined(__DOXYGEN__)*/
|
|
|
|
#include "gdisp/lld/gdisp_lld.h"
|
|
|
|
// Declare our driver object
|
|
GDISPDriver GDISP_Win32;
|
|
|
|
#define GC (&GDISP_Win32)
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <windows.h>
|
|
#include <wingdi.h>
|
|
#include <assert.h>
|
|
|
|
#ifndef GDISP_SCREEN_WIDTH
|
|
#define GDISP_SCREEN_WIDTH 640
|
|
#endif
|
|
#ifndef GDISP_SCREEN_HEIGHT
|
|
#define GDISP_SCREEN_HEIGHT 480
|
|
#endif
|
|
|
|
#if GINPUT_NEED_TOGGLE
|
|
/* Include toggle support code */
|
|
#include "ginput/lld/toggle.h"
|
|
|
|
const GToggleConfig GInputToggleConfigTable[GINPUT_TOGGLE_CONFIG_ENTRIES] = {
|
|
{0, 0xFF, 0x00, 0},
|
|
};
|
|
#endif
|
|
|
|
#if GINPUT_NEED_MOUSE
|
|
/* Include mouse support code */
|
|
#include "ginput/lld/mouse.h"
|
|
#endif
|
|
|
|
/*===========================================================================*/
|
|
/* Driver local routines . */
|
|
/*===========================================================================*/
|
|
|
|
#define WIN32_USE_MSG_REDRAW FALSE
|
|
#if GINPUT_NEED_TOGGLE
|
|
#define WIN32_BUTTON_AREA 16
|
|
#else
|
|
#define WIN32_BUTTON_AREA 0
|
|
#endif
|
|
|
|
#define APP_NAME "GDISP"
|
|
|
|
#define COLOR2BGR(c) ((((c) & 0xFF)<<16)|((c) & 0xFF00)|(((c)>>16) & 0xFF))
|
|
#define BGR2COLOR(c) COLOR2BGR(c)
|
|
|
|
static HWND winRootWindow = NULL;
|
|
static HDC dcBuffer = NULL;
|
|
static HBITMAP dcBitmap = NULL;
|
|
static HBITMAP dcOldBitmap;
|
|
static volatile bool_t isReady = FALSE;
|
|
static coord_t wWidth, wHeight;
|
|
|
|
#if GINPUT_NEED_MOUSE
|
|
static coord_t mousex, mousey;
|
|
static uint16_t mousebuttons;
|
|
#endif
|
|
#if GINPUT_NEED_TOGGLE
|
|
static uint8_t toggles = 0;
|
|
#endif
|
|
|
|
static LRESULT myWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HDC dc;
|
|
PAINTSTRUCT ps;
|
|
#if GINPUT_NEED_TOGGLE
|
|
HBRUSH hbrOn, hbrOff;
|
|
HPEN pen;
|
|
RECT rect;
|
|
HGDIOBJ old;
|
|
POINT p;
|
|
coord_t pos;
|
|
uint8_t bit;
|
|
#endif
|
|
|
|
switch (Msg) {
|
|
case WM_CREATE:
|
|
break;
|
|
case WM_LBUTTONDOWN:
|
|
#if GINPUT_NEED_MOUSE
|
|
if ((coord_t)HIWORD(lParam) < wHeight) {
|
|
mousebuttons |= GINPUT_MOUSE_BTN_LEFT;
|
|
goto mousemove;
|
|
}
|
|
#endif
|
|
#if GINPUT_NEED_TOGGLE
|
|
bit = 1 << ((coord_t)LOWORD(lParam)*8/wWidth);
|
|
toggles ^= bit;
|
|
rect.left = 0;
|
|
rect.right = wWidth;
|
|
rect.top = wHeight;
|
|
rect.bottom = wHeight + WIN32_BUTTON_AREA;
|
|
InvalidateRect(hWnd, &rect, FALSE);
|
|
UpdateWindow(hWnd);
|
|
#if GINPUT_TOGGLE_POLL_PERIOD == TIME_INFINITE
|
|
ginputToggleWakeup();
|
|
#endif
|
|
#endif
|
|
break;
|
|
case WM_LBUTTONUP:
|
|
#if GINPUT_NEED_TOGGLE
|
|
if ((toggles & 0x0F)) {
|
|
toggles &= ~0x0F;
|
|
rect.left = 0;
|
|
rect.right = wWidth;
|
|
rect.top = wHeight;
|
|
rect.bottom = wHeight + WIN32_BUTTON_AREA;
|
|
InvalidateRect(hWnd, &rect, FALSE);
|
|
UpdateWindow(hWnd);
|
|
#if GINPUT_TOGGLE_POLL_PERIOD == TIME_INFINITE
|
|
ginputToggleWakeup();
|
|
#endif
|
|
}
|
|
#endif
|
|
#if GINPUT_NEED_MOUSE
|
|
if ((coord_t)HIWORD(lParam) < wHeight) {
|
|
mousebuttons &= ~GINPUT_MOUSE_BTN_LEFT;
|
|
goto mousemove;
|
|
}
|
|
#endif
|
|
break;
|
|
#if GINPUT_NEED_MOUSE
|
|
case WM_MBUTTONDOWN:
|
|
if ((coord_t)HIWORD(lParam) < wHeight) {
|
|
mousebuttons |= GINPUT_MOUSE_BTN_MIDDLE;
|
|
goto mousemove;
|
|
}
|
|
break;
|
|
case WM_MBUTTONUP:
|
|
if ((coord_t)HIWORD(lParam) < wHeight) {
|
|
mousebuttons &= ~GINPUT_MOUSE_BTN_MIDDLE;
|
|
goto mousemove;
|
|
}
|
|
break;
|
|
case WM_RBUTTONDOWN:
|
|
if ((coord_t)HIWORD(lParam) < wHeight) {
|
|
mousebuttons |= GINPUT_MOUSE_BTN_RIGHT;
|
|
goto mousemove;
|
|
}
|
|
break;
|
|
case WM_RBUTTONUP:
|
|
if ((coord_t)HIWORD(lParam) < wHeight) {
|
|
mousebuttons &= ~GINPUT_MOUSE_BTN_RIGHT;
|
|
goto mousemove;
|
|
}
|
|
break;
|
|
case WM_MOUSEMOVE:
|
|
if ((coord_t)HIWORD(lParam) >= wHeight)
|
|
break;
|
|
mousemove:
|
|
mousex = (coord_t)LOWORD(lParam);
|
|
mousey = (coord_t)HIWORD(lParam);
|
|
#if GINPUT_MOUSE_POLL_PERIOD == TIME_INFINITE
|
|
ginputMouseWakeup();
|
|
#endif
|
|
break;
|
|
#endif
|
|
case WM_SYSKEYDOWN:
|
|
case WM_KEYDOWN:
|
|
case WM_SYSKEYUP:
|
|
case WM_KEYUP:
|
|
break;
|
|
case WM_CHAR:
|
|
case WM_DEADCHAR:
|
|
case WM_SYSCHAR:
|
|
case WM_SYSDEADCHAR:
|
|
break;
|
|
case WM_PAINT:
|
|
dc = BeginPaint(hWnd, &ps);
|
|
BitBlt(dc, ps.rcPaint.left, ps.rcPaint.top,
|
|
ps.rcPaint.right - ps.rcPaint.left,
|
|
(ps.rcPaint.bottom > wHeight ? wHeight : ps.rcPaint.bottom) - ps.rcPaint.top,
|
|
dcBuffer, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
|
|
#if GINPUT_NEED_TOGGLE
|
|
if (ps.rcPaint.bottom >= wHeight) {
|
|
pen = CreatePen(PS_SOLID, 1, COLOR2BGR(Black));
|
|
hbrOn = CreateSolidBrush(COLOR2BGR(Blue));
|
|
hbrOff = CreateSolidBrush(COLOR2BGR(Gray));
|
|
old = SelectObject(dc, pen);
|
|
MoveToEx(dc, 0, wHeight, &p);
|
|
LineTo(dc, wWidth, wHeight);
|
|
for(pos = 0, bit=1; pos < wWidth; pos=rect.right, bit <<= 1) {
|
|
rect.left = pos;
|
|
rect.right = pos + wWidth/8;
|
|
rect.top = wHeight;
|
|
rect.bottom = wHeight + WIN32_BUTTON_AREA;
|
|
FillRect(dc, &rect, (toggles & bit) ? hbrOn : hbrOff);
|
|
if (pos > 0) {
|
|
MoveToEx(dc, rect.left, rect.top, &p);
|
|
LineTo(dc, rect.left, rect.bottom);
|
|
}
|
|
}
|
|
DeleteObject(hbrOn);
|
|
DeleteObject(hbrOff);
|
|
SelectObject(dc, old);
|
|
}
|
|
#endif
|
|
EndPaint(hWnd, &ps);
|
|
break;
|
|
case WM_DESTROY:
|
|
PostQuitMessage(0);
|
|
SelectObject(dcBuffer, dcOldBitmap);
|
|
DeleteDC(dcBuffer);
|
|
DeleteObject(dcBitmap);
|
|
winRootWindow = NULL;
|
|
break;
|
|
default:
|
|
return DefWindowProc(hWnd, Msg, wParam, lParam);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void InitWindow(void) {
|
|
HANDLE hInstance;
|
|
WNDCLASS wc;
|
|
RECT rect;
|
|
HDC dc;
|
|
|
|
hInstance = GetModuleHandle(NULL);
|
|
|
|
wc.style = CS_HREDRAW | CS_VREDRAW; // | CS_OWNDC;
|
|
wc.lpfnWndProc = (WNDPROC)myWindowProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = hInstance;
|
|
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = APP_NAME;
|
|
RegisterClass(&wc);
|
|
|
|
rect.top = 0; rect.bottom = wHeight+WIN32_BUTTON_AREA;
|
|
rect.left = 0; rect.right = wWidth;
|
|
AdjustWindowRect(&rect, WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU, 0);
|
|
winRootWindow = CreateWindow(APP_NAME, "", WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU, 0, 0,
|
|
rect.right-rect.left, rect.bottom-rect.top, 0, 0, hInstance, NULL);
|
|
assert(winRootWindow != NULL);
|
|
|
|
|
|
GetClientRect(winRootWindow, &rect);
|
|
wWidth = rect.right-rect.left;
|
|
wHeight = rect.bottom - rect.top - WIN32_BUTTON_AREA;
|
|
|
|
dc = GetDC(winRootWindow);
|
|
dcBitmap = CreateCompatibleBitmap(dc, wWidth, wHeight);
|
|
dcBuffer = CreateCompatibleDC(dc);
|
|
ReleaseDC(winRootWindow, dc);
|
|
dcOldBitmap = SelectObject(dcBuffer, dcBitmap);
|
|
|
|
ShowWindow(winRootWindow, SW_SHOW);
|
|
UpdateWindow(winRootWindow);
|
|
isReady = TRUE;
|
|
}
|
|
|
|
static DECLARE_THREAD_STACK(waWindowThread, 1024);
|
|
static DECLARE_THREAD_FUNCTION(WindowThread, param) {
|
|
(void)param;
|
|
MSG msg;
|
|
|
|
InitWindow();
|
|
do {
|
|
gfxSleepMilliseconds(1);
|
|
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
} while (msg.message != WM_QUIT);
|
|
ExitProcess(0);
|
|
return msg.wParam;
|
|
}
|
|
|
|
/*===========================================================================*/
|
|
/* Driver exported functions. */
|
|
/*===========================================================================*/
|
|
|
|
/* ---- Required Routines ---- */
|
|
/*
|
|
The following 2 routines are required.
|
|
All other routines are optional.
|
|
*/
|
|
|
|
/**
|
|
* @brief Low level GDISP driver initialisation.
|
|
* @return TRUE if successful, FALSE on error.
|
|
*
|
|
* @notapi
|
|
*/
|
|
bool_t gdisp_lld_init(void) {
|
|
RECT rect;
|
|
gfxThreadHandle hth;
|
|
|
|
/* Set the window dimensions */
|
|
GetWindowRect(GetDesktopWindow(), &rect);
|
|
wWidth = rect.right - rect.left;
|
|
wHeight = rect.bottom - rect.top - WIN32_BUTTON_AREA;
|
|
if (wWidth > GDISP_SCREEN_WIDTH)
|
|
wWidth = GDISP_SCREEN_WIDTH;
|
|
if (wHeight > GDISP_SCREEN_HEIGHT)
|
|
wHeight = GDISP_SCREEN_HEIGHT;
|
|
|
|
/* Initialise the window */
|
|
if (!(hth = gfxThreadCreate(waWindowThread, sizeof(waWindowThread), HIGH_PRIORITY, WindowThread, 0))) {
|
|
fprintf(stderr, "Cannot create window thread\n");
|
|
exit(-1);
|
|
}
|
|
gfxThreadClose(hth);
|
|
while (!isReady)
|
|
Sleep(1);
|
|
|
|
/* Initialise the GDISP structure to match */
|
|
GC->g.Orientation = GDISP_ROTATE_0;
|
|
GC->g.Powermode = powerOn;
|
|
GC->g.Backlight = 100;
|
|
GC->g.Contrast = 50;
|
|
GC->g.Width = wWidth;
|
|
GC->g.Height = wHeight;
|
|
return TRUE;
|
|
}
|
|
|
|
#if GDISP_HARDWARE_DRAWPIXEL
|
|
void gdisp_lld_draw_pixel(void) {
|
|
HDC dcScreen;
|
|
int x, y;
|
|
COLORREF color;
|
|
|
|
color = COLOR2BGR(GC->p.color);
|
|
|
|
#if GDISP_NEED_CONTROL
|
|
switch(GC->g.Orientation) {
|
|
case GDISP_ROTATE_0:
|
|
x = GC->p.x;
|
|
y = GC->p.y;
|
|
break;
|
|
case GDISP_ROTATE_90:
|
|
x = GC->g.Height - 1 - GC->p.y;
|
|
y = GC->p.x;
|
|
break;
|
|
case GDISP_ROTATE_180:
|
|
x = GC->g.Width - 1 - GC->p.x;
|
|
y = GC->g.Height - 1 - GC->p.y;
|
|
break;
|
|
case GDISP_ROTATE_270:
|
|
x = GC->p.y;
|
|
y = GC->g.Width - 1 - GC->p.x;
|
|
break;
|
|
}
|
|
#else
|
|
x = GC->p.x;
|
|
y = GC->p.y;
|
|
#endif
|
|
|
|
// Draw the pixel on the screen and in the buffer.
|
|
dcScreen = GetDC(winRootWindow);
|
|
SetPixel(dcScreen, x, y, color);
|
|
SetPixel(dcBuffer, x, y, color);
|
|
ReleaseDC(winRootWindow, dcScreen);
|
|
}
|
|
#endif
|
|
|
|
/* ---- Optional Routines ---- */
|
|
|
|
#if 0
|
|
#if GDISP_HARDWARE_LINES
|
|
/**
|
|
* @brief Draw a line.
|
|
* @note Optional - The high level driver can emulate using software.
|
|
*
|
|
* @param[in] x0, y0 The start of the line
|
|
* @param[in] x1, y1 The end of the line
|
|
* @param[in] color The color of the line
|
|
*
|
|
* @notapi
|
|
*/
|
|
void gdisp_lld_draw_line(coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color) {
|
|
POINT p;
|
|
HPEN pen;
|
|
HDC dc;
|
|
HGDIOBJ old;
|
|
#if GDISP_NEED_CLIP
|
|
HRGN clip;
|
|
#endif
|
|
#if WIN32_USE_MSG_REDRAW
|
|
RECT rect;
|
|
#endif
|
|
#if GDISP_NEED_CONTROL
|
|
coord_t t;
|
|
#endif
|
|
|
|
#if GDISP_NEED_CLIP
|
|
clip = NULL;
|
|
#endif
|
|
|
|
#if GDISP_NEED_CONTROL
|
|
switch(GC->g.Orientation) {
|
|
case GDISP_ROTATE_0:
|
|
#if GDISP_NEED_CLIP
|
|
// Clip post orientation change
|
|
if (GC->g.clipx0 != 0 || GC->g.clipy0 != 0 || GC->g.clipx1 != GC->g.Width || GC->g.clipy1 != GC->g.Height)
|
|
clip = CreateRectRgn(GC->g.clipx0, GC->g.clipy0, GC->g.clipx1, GC->g.clipy1);
|
|
#endif
|
|
break;
|
|
case GDISP_ROTATE_90:
|
|
t = GC->g.Height - 1 - y0;
|
|
y0 = x0;
|
|
x0 = t;
|
|
t = GC->g.Height - 1 - y1;
|
|
y1 = x1;
|
|
x1 = t;
|
|
#if GDISP_NEED_CLIP
|
|
// Clip post orientation change
|
|
if (GC->g.clipx0 != 0 || GC->g.clipy0 != 0 || GC->g.clipx1 != GC->g.Width || GC->g.clipy1 != GC->g.Height)
|
|
clip = CreateRectRgn(GC->g.Height-1-GC->g.clipy1, GC->g.clipx0, GC->g.Height-1-GC->g.clipy0, GC->g.clipx1);
|
|
#endif
|
|
break;
|
|
case GDISP_ROTATE_180:
|
|
x0 = GC->g.Width - 1 - x0;
|
|
y0 = GC->g.Height - 1 - y0;
|
|
x1 = GC->g.Width - 1 - x1;
|
|
y1 = GC->g.Height - 1 - y1;
|
|
#if GDISP_NEED_CLIP
|
|
// Clip post orientation change
|
|
if (GC->g.clipx0 != 0 || GC->g.clipy0 != 0 || GC->g.clipx1 != GC->g.Width || GC->g.clipy1 != GC->g.Height)
|
|
clip = CreateRectRgn(GC->g.Width-1-GC->g.clipx1, GC->g.Height-1-GC->g.clipy1, GC->g.Width-1-GC->g.clipx0, GC->g.Height-1-GC->g.clipy0);
|
|
#endif
|
|
break;
|
|
case GDISP_ROTATE_270:
|
|
t = GC->g.Width - 1 - x0;
|
|
x0 = y0;
|
|
y0 = t;
|
|
t = GC->g.Width - 1 - x1;
|
|
x1 = y1;
|
|
y1 = t;
|
|
#if GDISP_NEED_CLIP
|
|
// Clip post orientation change
|
|
if (GC->g.clipx0 != 0 || GC->g.clipy0 != 0 || GC->g.clipx1 != GC->g.Width || GC->g.clipy1 != GC->g.Height)
|
|
clip = CreateRectRgn(GC->g.clipy0, GC->g.Width-1-GC->g.clipx1, GC->g.clipy1, GC->g.Width-1-GC->g.clipx0);
|
|
#endif
|
|
break;
|
|
}
|
|
#else
|
|
#if GDISP_NEED_CLIP
|
|
clip = NULL;
|
|
if (GC->g.clipx0 != 0 || GC->g.clipy0 != 0 || GC->g.clipx1 != GC->g.Width || GC->g.clipy1 != GC->g.Height)
|
|
clip = CreateRectRgn(GC->g.clipx0, GC->g.clipy0, GC->g.clipx1, GC->g.clipy1);
|
|
#endif
|
|
#endif
|
|
|
|
color = COLOR2BGR(color);
|
|
pen = CreatePen(PS_SOLID, 1, color);
|
|
if (pen) {
|
|
// Draw the line in the buffer
|
|
#if GDISP_NEED_CLIP
|
|
if (clip) SelectClipRgn(dcBuffer, clip);
|
|
#endif
|
|
old = SelectObject(dcBuffer, pen);
|
|
MoveToEx(dcBuffer, x0, y0, &p);
|
|
LineTo(dcBuffer, x1, y1);
|
|
SelectObject(dcBuffer, old);
|
|
SetPixel(dcBuffer, x1, y1, color);
|
|
#if GDISP_NEED_CLIP
|
|
if (clip) SelectClipRgn(dcBuffer, NULL);
|
|
#endif
|
|
|
|
#if WIN32_USE_MSG_REDRAW
|
|
rect.left = x0; rect.right = x1+1;
|
|
rect.top = y0; rect.bottom = y1+1;
|
|
InvalidateRect(winRootWindow, &rect, FALSE);
|
|
UpdateWindow(winRootWindow);
|
|
#else
|
|
// Redrawing the line on the screen is cheaper than invalidating the whole rectangular area
|
|
dc = GetDC(winRootWindow);
|
|
#if GDISP_NEED_CLIP
|
|
if (clip) SelectClipRgn(dc, clip);
|
|
#endif
|
|
old = SelectObject(dc, pen);
|
|
MoveToEx(dc, x0, y0, &p);
|
|
LineTo(dc, x1, y1);
|
|
SelectObject(dc, old);
|
|
SetPixel(dc, x1, y1, color);
|
|
#if GDISP_NEED_CLIP
|
|
if (clip) SelectClipRgn(dc, NULL);
|
|
#endif
|
|
ReleaseDC(winRootWindow, dc);
|
|
#endif
|
|
|
|
DeleteObject(pen);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if GDISP_HARDWARE_FILLS
|
|
void gdisp_lld_fill_area(void) {
|
|
HDC dcScreen;
|
|
RECT rect;
|
|
HBRUSH hbr;
|
|
COLORREF color;
|
|
|
|
color = COLOR2BGR(GC->p.color);
|
|
#if GDISP_NEED_CONTROL
|
|
switch(GC->g.Orientation) {
|
|
case GDISP_ROTATE_0:
|
|
rect.top = GC->p.y;
|
|
rect.bottom = rect.top + GC->p.cy;
|
|
rect.left = GC->p.x;
|
|
rect.right = rect.left + GC->p.cx;
|
|
break;
|
|
case GDISP_ROTATE_90:
|
|
rect.top = GC->p.x;
|
|
rect.bottom = rect.top + GC->p.cx;
|
|
rect.right = GC->g.Height - GC->p.y;
|
|
rect.left = rect.right - GC->p.cy;
|
|
break;
|
|
case GDISP_ROTATE_180:
|
|
rect.bottom = GC->g.Height - GC->p.y;
|
|
rect.top = rect.bottom - GC->p.cy;
|
|
rect.right = GC->g.Width - GC->p.x;
|
|
rect.left = rect.right - GC->p.cx;
|
|
break;
|
|
case GDISP_ROTATE_270:
|
|
rect.bottom = GC->g.Width - GC->p.x;
|
|
rect.top = rect.bottom - GC->p.cx;
|
|
rect.left = GC->p.y;
|
|
rect.right = rect.left + GC->p.cy;
|
|
break;
|
|
}
|
|
#else
|
|
rect.top = GC->p.y;
|
|
rect.bottom = rect.top + GC->p.cy;
|
|
rect.left = GC->p.x;
|
|
rect.right = rect.left + GC->p.cx;
|
|
#endif
|
|
|
|
hbr = CreateSolidBrush(color);
|
|
|
|
dcScreen = GetDC(winRootWindow);
|
|
FillRect(dcScreen, &rect, hbr);
|
|
FillRect(dcBuffer, &rect, hbr);
|
|
ReleaseDC(winRootWindow, dcScreen);
|
|
|
|
DeleteObject(hbr);
|
|
}
|
|
#endif
|
|
|
|
#if (GDISP_HARDWARE_BITFILLS && GDISP_NEED_CONTROL) || defined(__DOXYGEN__)
|
|
static pixel_t *rotateimg(coord_t cx, coord_t cy, coord_t srcx, coord_t srccx, const pixel_t *buffer) {
|
|
pixel_t *dstbuf;
|
|
pixel_t *dst;
|
|
const pixel_t *src;
|
|
size_t sz;
|
|
coord_t i, j;
|
|
|
|
// Shortcut.
|
|
if (GC->g.Orientation == GDISP_ROTATE_0 && srcx == 0 && cx == srccx)
|
|
return (pixel_t *)buffer;
|
|
|
|
// Allocate the destination buffer
|
|
sz = (size_t)cx * (size_t)cy;
|
|
if (!(dstbuf = (pixel_t *)malloc(sz * sizeof(pixel_t))))
|
|
return 0;
|
|
|
|
// Copy the bits we need
|
|
switch(GC->g.Orientation) {
|
|
case GDISP_ROTATE_0:
|
|
for(dst = dstbuf, src = buffer+srcx, j = 0; j < cy; j++)
|
|
for(i = 0; i < cx; i++, src += srccx - cx)
|
|
*dst++ = *src++;
|
|
break;
|
|
case GDISP_ROTATE_90:
|
|
for(src = buffer+srcx, j = 0; j < cy; j++) {
|
|
dst = dstbuf+cy-j-1;
|
|
for(i = 0; i < cx; i++, src += srccx - cx, dst += cy)
|
|
*dst = *src++;
|
|
}
|
|
break;
|
|
case GDISP_ROTATE_180:
|
|
for(dst = dstbuf+sz, src = buffer+srcx, j = 0; j < cy; j++)
|
|
for(i = 0; i < cx; i++, src += srccx - cx)
|
|
*--dst = *src++;
|
|
break;
|
|
case GDISP_ROTATE_270:
|
|
for(src = buffer+srcx, j = 0; j < cy; j++) {
|
|
dst = dstbuf+sz-cy+j;
|
|
for(i = 0; i < cx; i++, src += srccx - cx, dst -= cy)
|
|
*dst = *src++;
|
|
}
|
|
break;
|
|
}
|
|
return dstbuf;
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_HARDWARE_BITFILLS
|
|
/**
|
|
* @brief Fill an area with a bitmap.
|
|
* @note Optional - The high level driver can emulate using software.
|
|
*
|
|
* @param[in] x, y The start filled area
|
|
* @param[in] cx, cy The width and height to be filled
|
|
* @param[in] srcx, srcy The bitmap position to start the fill from
|
|
* @param[in] srccx The width of a line in the bitmap.
|
|
* @param[in] buffer The pixels to use to fill the area.
|
|
*
|
|
* @notapi
|
|
*/
|
|
void gdisp_lld_blit_area_ex(coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer) {
|
|
BITMAPV4HEADER bmpInfo;
|
|
RECT rect;
|
|
#if GDISP_NEED_CONTROL
|
|
pixel_t *srcimg;
|
|
#endif
|
|
|
|
#if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
|
|
// Clip pre orientation change
|
|
if (x < GC->g.clipx0) { cx -= GC->g.clipx0 - x; srcx += GC->g.clipx0 - x; x = GC->g.clipx0; }
|
|
if (y < GC->g.clipy0) { cy -= GC->g.clipy0 - y; srcy += GC->g.clipy0 - y; y = GC->g.clipy0; }
|
|
if (srcx+cx > srccx) cx = srccx - srcx;
|
|
if (cx <= 0 || cy <= 0 || x >= GC->g.clipx1 || y >= GC->g.clipy1) return;
|
|
if (x+cx > GC->g.clipx1) cx = GC->g.clipx1 - x;
|
|
if (y+cy > GC->g.clipy1) cy = GC->g.clipy1 - y;
|
|
#endif
|
|
|
|
// Make everything relative to the start of the line
|
|
buffer += srccx*srcy;
|
|
srcy = 0;
|
|
|
|
memset(&bmpInfo, 0, sizeof(bmpInfo));
|
|
bmpInfo.bV4Size = sizeof(bmpInfo);
|
|
bmpInfo.bV4Planes = 1;
|
|
bmpInfo.bV4BitCount = 32;
|
|
bmpInfo.bV4AlphaMask = 0;
|
|
bmpInfo.bV4RedMask = RGB2COLOR(255,0,0);
|
|
bmpInfo.bV4GreenMask = RGB2COLOR(0,255,0);
|
|
bmpInfo.bV4BlueMask = RGB2COLOR(0,0,255);
|
|
bmpInfo.bV4V4Compression = BI_BITFIELDS;
|
|
bmpInfo.bV4XPelsPerMeter = 3078;
|
|
bmpInfo.bV4YPelsPerMeter = 3078;
|
|
bmpInfo.bV4ClrUsed = 0;
|
|
bmpInfo.bV4ClrImportant = 0;
|
|
bmpInfo.bV4CSType = 0; //LCS_sRGB;
|
|
|
|
#if GDISP_NEED_CONTROL
|
|
bmpInfo.bV4SizeImage = (cy*cx) * sizeof(pixel_t);
|
|
srcimg = rotateimg(cx, cy, srcx, srccx, buffer);
|
|
if (!srcimg) return;
|
|
|
|
switch(GC->g.Orientation) {
|
|
case GDISP_ROTATE_0:
|
|
bmpInfo.bV4Width = cx;
|
|
bmpInfo.bV4Height = -cy; /* top-down image */
|
|
rect.top = y;
|
|
rect.bottom = rect.top+cy;
|
|
rect.left = x;
|
|
rect.right = rect.left+cx;
|
|
break;
|
|
case GDISP_ROTATE_90:
|
|
bmpInfo.bV4Width = cy;
|
|
bmpInfo.bV4Height = -cx; /* top-down image */
|
|
rect.top = x;
|
|
rect.bottom = rect.top+cx;
|
|
rect.right = GC->g.Height - y;
|
|
rect.left = rect.right-cy;
|
|
break;
|
|
case GDISP_ROTATE_180:
|
|
bmpInfo.bV4Width = cx;
|
|
bmpInfo.bV4Height = -cy; /* top-down image */
|
|
rect.bottom = GC->g.Height - y;
|
|
rect.top = rect.bottom-cy;
|
|
rect.right = GC->g.Width - x;
|
|
rect.left = rect.right-cx;
|
|
break;
|
|
case GDISP_ROTATE_270:
|
|
bmpInfo.bV4Width = cy;
|
|
bmpInfo.bV4Height = -cx; /* top-down image */
|
|
rect.bottom = GC->g.Width - x;
|
|
rect.top = rect.bottom-cx;
|
|
rect.left = y;
|
|
rect.right = rect.left+cy;
|
|
break;
|
|
}
|
|
SetDIBitsToDevice(dcBuffer, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0, 0, 0, rect.bottom-rect.top, srcimg, (BITMAPINFO*)&bmpInfo, DIB_RGB_COLORS);
|
|
if (srcimg != (pixel_t *)buffer)
|
|
free(srcimg);
|
|
|
|
#else
|
|
bmpInfo.bV4Width = srccx;
|
|
bmpInfo.bV4Height = -cy; /* top-down image */
|
|
bmpInfo.bV4SizeImage = (cy*srccx) * sizeof(pixel_t);
|
|
rect.top = y;
|
|
rect.bottom = rect.top+cy;
|
|
rect.left = x;
|
|
rect.right = rect.left+cx;
|
|
SetDIBitsToDevice(dcBuffer, x, y, cx, cy, srcx, 0, 0, cy, buffer, (BITMAPINFO*)&bmpInfo, DIB_RGB_COLORS);
|
|
#endif
|
|
|
|
// Invalidate the region to get it on the screen.
|
|
InvalidateRect(winRootWindow, &rect, FALSE);
|
|
UpdateWindow(winRootWindow);
|
|
}
|
|
#endif
|
|
|
|
#if (GDISP_NEED_PIXELREAD && GDISP_HARDWARE_PIXELREAD) || defined(__DOXYGEN__)
|
|
/**
|
|
* @brief Get the color of a particular pixel.
|
|
* @note Optional.
|
|
* @note If x,y is off the screen, the result is undefined.
|
|
* @return The color of the specified pixel.
|
|
*
|
|
* @param[in] x, y The start of the text
|
|
*
|
|
* @notapi
|
|
*/
|
|
color_t gdisp_lld_get_pixel_color(coord_t x, coord_t y) {
|
|
color_t color;
|
|
#if GDISP_NEED_CONTROL
|
|
coord_t t;
|
|
#endif
|
|
|
|
#if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
|
|
// Clip pre orientation change
|
|
if (x < 0 || x >= GC->g.Width || y < 0 || y >= GC->g.Height) return 0;
|
|
#endif
|
|
|
|
#if GDISP_NEED_CONTROL
|
|
switch(GC->g.Orientation) {
|
|
case GDISP_ROTATE_0:
|
|
break;
|
|
case GDISP_ROTATE_90:
|
|
t = GC->g.Height - 1 - y;
|
|
y = x;
|
|
x = t;
|
|
break;
|
|
case GDISP_ROTATE_180:
|
|
x = GC->g.Width - 1 - x;
|
|
y = GC->g.Height - 1 - y;
|
|
break;
|
|
case GDISP_ROTATE_270:
|
|
t = GC->g.Width - 1 - x;
|
|
x = y;
|
|
y = t;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
color = GetPixel(dcBuffer, x, y);
|
|
return BGR2COLOR(color);
|
|
}
|
|
#endif
|
|
|
|
#if (GDISP_NEED_SCROLL && GDISP_HARDWARE_SCROLL) || defined(__DOXYGEN__)
|
|
/**
|
|
* @brief Scroll vertically a section of the screen.
|
|
* @note Optional.
|
|
* @note If x,y + cx,cy is off the screen, the result is undefined.
|
|
* @note If lines is >= cy, it is equivelent to a area fill with bgcolor.
|
|
*
|
|
* @param[in] x, y The start of the area to be scrolled
|
|
* @param[in] cx, cy The size of the area to be scrolled
|
|
* @param[in] lines The number of lines to scroll (Can be positive or negative)
|
|
* @param[in] bgcolor The color to fill the newly exposed area.
|
|
*
|
|
* @notapi
|
|
*/
|
|
void gdisp_lld_vertical_scroll(coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor) {
|
|
RECT rect, frect, srect;
|
|
HBRUSH hbr;
|
|
|
|
#if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
|
|
// Clip pre orientation change
|
|
if (x < GC->g.clipx0) { cx -= GC->g.clipx0 - x; x = GC->g.clipx0; }
|
|
if (y < GC->g.clipy0) { cy -= GC->g.clipy0 - y; y = GC->g.clipy0; }
|
|
if (!lines || cx <= 0 || cy <= 0 || x >= GC->g.clipx1 || y >= GC->g.clipy1) return;
|
|
if (x+cx > GC->g.clipx1) cx = GC->g.clipx1 - x;
|
|
if (y+cy > GC->g.clipy1) cy = GC->g.clipy1 - y;
|
|
#endif
|
|
|
|
if (lines > cy) lines = cy;
|
|
else if (-lines > cy) lines = -cy;
|
|
|
|
bgcolor = COLOR2BGR(bgcolor);
|
|
hbr = CreateSolidBrush(bgcolor);
|
|
|
|
#if GDISP_NEED_CONTROL
|
|
switch(GC->g.Orientation) {
|
|
case GDISP_ROTATE_0:
|
|
rect.top = y;
|
|
rect.bottom = rect.top+cy;
|
|
rect.left = x;
|
|
rect.right = rect.left+cx;
|
|
lines = -lines;
|
|
goto vertical_scroll;
|
|
case GDISP_ROTATE_90:
|
|
rect.top = x;
|
|
rect.bottom = rect.top+cx;
|
|
rect.right = GC->g.Height - y;
|
|
rect.left = rect.right-cy;
|
|
goto horizontal_scroll;
|
|
case GDISP_ROTATE_180:
|
|
rect.bottom = GC->g.Height - y;
|
|
rect.top = rect.bottom-cy;
|
|
rect.right = GC->g.Width - x;
|
|
rect.left = rect.right-cx;
|
|
vertical_scroll:
|
|
srect.left = frect.left = rect.left;
|
|
srect.right = frect.right = rect.right;
|
|
if (lines > 0) {
|
|
srect.top = frect.top = rect.top;
|
|
frect.bottom = rect.top+lines;
|
|
srect.bottom = rect.bottom-lines;
|
|
} else {
|
|
srect.bottom = frect.bottom = rect.bottom;
|
|
frect.top = rect.bottom+lines;
|
|
srect.top = rect.top-lines;
|
|
}
|
|
if (cy >= lines && cy >= -lines)
|
|
ScrollDC(dcBuffer, 0, lines, &srect, 0, 0, 0);
|
|
break;
|
|
case GDISP_ROTATE_270:
|
|
rect.bottom = GC->g.Width - x;
|
|
rect.top = rect.bottom-cx;
|
|
rect.left = y;
|
|
rect.right = rect.left+cy;
|
|
lines = -lines;
|
|
horizontal_scroll:
|
|
srect.top = frect.top = rect.top;
|
|
srect.bottom = frect.bottom = rect.bottom;
|
|
if (lines > 0) {
|
|
srect.left = frect.left = rect.left;
|
|
frect.right = rect.left+lines;
|
|
srect.right = rect.right-lines;
|
|
} else {
|
|
srect.right = frect.right = rect.right;
|
|
frect.left = rect.right+lines;
|
|
srect.left = rect.left-lines;
|
|
}
|
|
if (cy >= lines && cy >= -lines)
|
|
ScrollDC(dcBuffer, lines, 0, &srect, 0, 0, 0);
|
|
break;
|
|
}
|
|
#else
|
|
rect.top = y;
|
|
rect.bottom = rect.top+cy;
|
|
rect.left = x;
|
|
rect.right = rect.left+cx;
|
|
lines = -lines;
|
|
srect.left = frect.left = rect.left;
|
|
srect.right = frect.right = rect.right;
|
|
if (lines > 0) {
|
|
srect.top = frect.top = rect.top;
|
|
frect.bottom = rect.top+lines;
|
|
srect.bottom = rect.bottom-lines;
|
|
} else {
|
|
srect.bottom = frect.bottom = rect.bottom;
|
|
frect.top = rect.bottom+lines;
|
|
srect.top = rect.top-lines;
|
|
}
|
|
if (cy >= lines && cy >= -lines)
|
|
ScrollDC(dcBuffer, 0, lines, &srect, 0, 0, 0);
|
|
#endif
|
|
|
|
if (hbr)
|
|
FillRect(dcBuffer, &frect, hbr);
|
|
InvalidateRect(winRootWindow, &rect, FALSE);
|
|
UpdateWindow(winRootWindow);
|
|
}
|
|
#endif
|
|
|
|
#if (GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL) || defined(__DOXYGEN__)
|
|
/**
|
|
* @brief Driver Control
|
|
* @detail Unsupported control codes are ignored.
|
|
* @note The value parameter should always be typecast to (void *).
|
|
* @note There are some predefined and some specific to the low level driver.
|
|
* @note GDISP_CONTROL_POWER - Takes a gdisp_powermode_t
|
|
* GDISP_CONTROL_ORIENTATION - Takes a gdisp_orientation_t
|
|
* GDISP_CONTROL_BACKLIGHT - Takes an int from 0 to 100. For a driver
|
|
* that only supports off/on anything other
|
|
* than zero is on.
|
|
* GDISP_CONTROL_CONTRAST - Takes an int from 0 to 100.
|
|
* GDISP_CONTROL_LLD - Low level driver control constants start at
|
|
* this value.
|
|
*
|
|
* @param[in] what What to do.
|
|
* @param[in] value The value to use (always cast to a void *).
|
|
*
|
|
* @notapi
|
|
*/
|
|
void gdisp_lld_control(unsigned what, void *value) {
|
|
switch(what) {
|
|
case GDISP_CONTROL_ORIENTATION:
|
|
if (GC->g.Orientation == (gdisp_orientation_t)value)
|
|
return;
|
|
switch((gdisp_orientation_t)value) {
|
|
case GDISP_ROTATE_0:
|
|
GC->g.Width = wWidth;
|
|
GC->g.Height = wHeight;
|
|
break;
|
|
case GDISP_ROTATE_90:
|
|
GC->g.Height = wWidth;
|
|
GC->g.Width = wHeight;
|
|
break;
|
|
case GDISP_ROTATE_180:
|
|
GC->g.Width = wWidth;
|
|
GC->g.Height = wHeight;
|
|
break;
|
|
case GDISP_ROTATE_270:
|
|
GC->g.Height = wWidth;
|
|
GC->g.Width = wHeight;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
#if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION
|
|
GC->g.clipx0 = 0;
|
|
GC->g.clipy0 = 0;
|
|
GC->g.clipx1 = GC->g.Width;
|
|
GC->g.clipy1 = GC->g.Height;
|
|
#endif
|
|
GC->g.Orientation = (gdisp_orientation_t)value;
|
|
return;
|
|
/*
|
|
case GDISP_CONTROL_POWER:
|
|
case GDISP_CONTROL_BACKLIGHT:
|
|
case GDISP_CONTROL_CONTRAST:
|
|
*/
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if GINPUT_NEED_MOUSE
|
|
|
|
void ginput_lld_mouse_init(void) {}
|
|
|
|
void ginput_lld_mouse_get_reading(MouseReading *pt) {
|
|
pt->x = mousex;
|
|
pt->y = mousey > wHeight ? wHeight : mousey;
|
|
pt->z = (mousebuttons & GINPUT_MOUSE_BTN_LEFT) ? 100 : 0;
|
|
pt->buttons = mousebuttons;
|
|
}
|
|
|
|
#endif /* GINPUT_NEED_MOUSE */
|
|
|
|
#if GINPUT_NEED_TOGGLE
|
|
|
|
void ginput_lld_toggle_init(const GToggleConfig *ptc) { (void) ptc; }
|
|
unsigned ginput_lld_toggle_getbits(const GToggleConfig *ptc) { (void) ptc; return toggles; }
|
|
|
|
#endif /* GINPUT_NEED_MOUSE */
|
|
|
|
#endif /* GFX_USE_GDISP */
|
|
/** @} */
|
|
|