/* * 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 . * */ #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; y1height; 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; }