kpmpgsmkii/client/src/system/surface.c
2021-11-30 20:29:58 -06:00

252 lines
5.8 KiB
C

/*
* Kangaroo Punch MultiPlayer Game Server Mark II
* Copyright (C) 2020-2021 Scott Duensing
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*
*/
#include "surface.h"
#include "vesa.h"
static SurfaceT *_offScreenSurface;
static SurfaceT *_activeSurface;
static void surfacePutPixel8(uint16_t x, uint16_t y, ColorT color);
static void surfacePutPixel16(uint16_t x, uint16_t y, ColorT color);
static void surfacePutPixel32(uint16_t x, uint16_t y, ColorT color);
void (*surfacePutPixel)(uint16_t x, uint16_t y, ColorT color);
static void surfacePutPixel8(uint16_t x, uint16_t y, ColorT color) {
_activeSurface->buffer.bits8[y * _activeSurface->width + x] = (uint8_t)color;
}
static void surfacePutPixel16(uint16_t x, uint16_t y, ColorT color) {
_activeSurface->buffer.bits16[y * _activeSurface->width + x] = (uint16_t)color;
}
static void surfacePutPixel32(uint16_t x, uint16_t y, ColorT color) {
_activeSurface->buffer.bits32[y * _activeSurface->width + x] = color;
}
void surfaceBlit(SurfaceT *source, uint16_t x, uint16_t y) {
uint16_t y1;
size_t offsetTarget;
size_t offsetSource;
if (x == 0 && y == 0 && _activeSurface->width == source->width && _activeSurface->height == source->height) {
// Direct blit of entire surface.
memcpy(_activeSurface->buffer.bits8, source->buffer.bits8, source->bytes);
} else {
// Blit into larger surface.
offsetTarget = y * _activeSurface->scanline + x * vbeDisplayDepthBytesGet();
offsetSource = 0;
for (y1=y; y1<y+source->height; y1++) {
memcpy(&_activeSurface->buffer.bits8[offsetTarget], &source->buffer.bits8[offsetSource], source->scanline);
offsetTarget += _activeSurface->scanline;
offsetSource += source->scanline;
}
}
}
void surfaceClear(ColorT color) {
uint16_t x;
size_t offsetTarget;
// Draw the top line.
for (x=0; x<_activeSurface->width; x++) {
surfacePutPixel(x, 0, color);
}
// Copy it to the other lines.
offsetTarget = _activeSurface->scanline;
for (x=1; x<_activeSurface->height; x++) {
memcpy(&_activeSurface->buffer.bits8[offsetTarget], &_activeSurface->buffer.bits8[0], _activeSurface->scanline);
offsetTarget += _activeSurface->scanline;
}
}
SurfaceT *surfaceCreate(uint16_t width, uint16_t height) {
SurfaceT *surface = (SurfaceT *)malloc(sizeof(SurfaceT));
if (!surface) return NULL;
surface->width = width;
surface->height = height;
surface->scanline = width * vbeDisplayDepthBytesGet();
surface->bytes = surface->scanline * height;
surface->buffer.bits8 = malloc(surface->bytes);
if (!surface->buffer.bits8) {
free(surface);
return NULL;
}
memset(surface->buffer.bits8, 0, surface->bytes);
return surface;
}
void surfaceDestroy(SurfaceT **surface) {
SurfaceT *s = *surface;
free(s->buffer.bits8);
free(s);
s = NULL;
}
uint16_t surfaceHeightGet(void) {
return _activeSurface->height;
}
void surfaceHighlightFrameDraw(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, ColorT upperLeft, ColorT lowerRight) {
surfaceLineDraw(x1, y1, x2, y1, upperLeft);
surfaceLineDraw(x1, y1, x1, y2, upperLeft);
surfaceLineDraw(x1, y2, x2, y2, lowerRight);
surfaceLineDraw(x2, y1, x2, y2, lowerRight);
}
void surfaceLineDraw(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color) {
int16_t x;
int16_t y;
int16_t dx;
int16_t dy;
int16_t incX;
int16_t incY;
int16_t balance;
if (x2 >= x1) {
dx = x2 - x1;
incX = 1;
} else {
dx = x1 - x2;
incX = -1;
}
if (y2 >= y1) {
dy = y2 - y1;
incY = 1;
} else {
dy = y1 - y2;
incY = -1;
}
x = x1;
y = y1;
if (dx >= dy) {
dy <<= 1;
balance = dy - dx;
dx <<= 1;
while (x != x2) {
surfacePutPixel(x, y, color);
if (balance >= 0) {
y += incY;
balance -= dx;
}
balance += dy;
x += incX;
}
surfacePutPixel(x, y, color);
} else {
dx <<= 1;
balance = dx - dy;
dy <<= 1;
while (y != y2) {
surfacePutPixel(x, y, color);
if (balance >= 0) {
x += incX;
balance -= dy;
}
balance += dx;
y += incY;
}
surfacePutPixel(x, y, color);
}
}
SurfaceT *surfaceOffScreenGet(void) {
return _offScreenSurface;
}
void surfaceRectangleDraw(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color) {
surfaceLineDraw(x1, y1, x2, y1, color);
surfaceLineDraw(x2, y1, x2, y2, color);
surfaceLineDraw(x1, y2, x2, y2, color);
surfaceLineDraw(x1, y1, x1, y2, color);
}
void surfaceRectangleFilledDraw(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color) {
uint16_t x;
uint16_t y;
// This could be optimized similar to surfaceClear
for (y=y1; y<=y2; y++) {
for (x=x1; x<=x2; x++) {
surfacePutPixel(x, y, color);
}
}
}
void surfaceSet(SurfaceT *surface) {
if (surface) {
_activeSurface = surface;
} else {
_activeSurface = _offScreenSurface;
}
}
void surfaceShutdown(void) {
surfaceDestroy(&_offScreenSurface);
}
uint8_t surfaceStartup(void) {
_offScreenSurface = surfaceCreate(vbeDisplayWidthGet(), vbeDisplayHeightGet());
if (vbeDisplayDepthBitsGet() == 8) surfacePutPixel = surfacePutPixel8;
if (vbeDisplayDepthBitsGet() == 16) surfacePutPixel = surfacePutPixel16;
if (vbeDisplayDepthBitsGet() == 15) surfacePutPixel = surfacePutPixel16;
if (vbeDisplayDepthBitsGet() == 32) surfacePutPixel = surfacePutPixel32;
return (_offScreenSurface == NULL ? 1 : 0);
}
uint16_t surfaceWidthGet(void) {
return _activeSurface->width;
}