317 lines
10 KiB
C++
317 lines
10 KiB
C++
#include "grafix.h"
|
|
#include <SDL2/SDL.h>
|
|
#include "gif.c"
|
|
#include "crt.h"
|
|
|
|
namespace draw
|
|
{
|
|
SDL_Window *sdl_window = nullptr; // La finestra de SDL
|
|
SDL_Renderer *sdl_renderer = nullptr; // El renderer de SDL
|
|
SDL_Texture *sdl_texture = nullptr; // La textura de SDL a la que pintarem la nostra superficie "screen" i que despres volcarem a pantalla
|
|
|
|
surface *screen = nullptr; // La superficie screen, que representa la pantalla. Se crea i destrueix internament
|
|
uint32_t palette[256]; // La paleta de colors
|
|
|
|
void init(const char* titol, const uint16_t width, const uint16_t height, const uint8_t zoom)
|
|
{
|
|
// Inicialització de les estructures de SDL
|
|
sdl_window = SDL_CreateWindow(titol, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width * zoom, height * zoom, SDL_WINDOW_SHOWN);
|
|
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
|
|
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
|
|
|
|
// Establim el tamany "logic", indepndent del tamany de finestra
|
|
SDL_RenderSetLogicalSize(sdl_renderer, width, height);
|
|
|
|
// Creem la superficie "screen" i la establim com a superficie destinació
|
|
screen = createSurface(width, height);
|
|
}
|
|
|
|
// Finalització del sistema
|
|
void quit()
|
|
{
|
|
// Si la superficie "screen" existia, alliberem la seua memòria
|
|
if (screen != nullptr)
|
|
{
|
|
freeSurface(screen);
|
|
}
|
|
|
|
// Destruim tot el relacionat amb SDL
|
|
SDL_DestroyTexture(sdl_texture);
|
|
SDL_DestroyRenderer(sdl_renderer);
|
|
SDL_DestroyWindow(sdl_window);
|
|
|
|
// Fiquem tots els punters a nullptr, per si de cas no estem eixint del programa
|
|
// i anem a tornar a inicialitzar el sistema
|
|
sdl_window = nullptr;
|
|
sdl_renderer = nullptr;
|
|
sdl_texture = nullptr;
|
|
screen = nullptr;
|
|
}
|
|
|
|
surface *createSurface(const int width, const int height)
|
|
{
|
|
// Primer reservem memòria per a la estructura "surface"
|
|
surface *surf = (surface *)malloc(sizeof(surface));
|
|
|
|
// Després reservem memòria per als pixels
|
|
surf->pixels = (uint8_t *)malloc(width * height);
|
|
|
|
// I apuntem el ample i alt de la superficie
|
|
surf->w = width;
|
|
surf->h = height;
|
|
|
|
// ...i tornem la superficie creada, clar
|
|
return surf;
|
|
}
|
|
|
|
surface *loadSurface(const char *filename)
|
|
{
|
|
// Agafem un buffer de bytes de l'arxiu especificat
|
|
FILE *f = fopen(filename, "r");
|
|
if (f==nullptr) return nullptr;
|
|
fseek(f, 0, SEEK_END);
|
|
const int size = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
uint8_t *buffer = (uint8_t*)malloc(size);
|
|
fread(buffer, size, 1, f);
|
|
fclose(f);
|
|
|
|
// Primer reservem memòria per a la estructura "surface"
|
|
surface *surf = (surface *)malloc(sizeof(surface));
|
|
|
|
// Després li passem el buffer de bytes a la funció de carregar un GIF.
|
|
// El resultat es un array de bytes, els pixels en sí. Ja havem reservat
|
|
// la memòria necessaria en "LoadGif", així que no tenim que fer-ho ara,
|
|
// però, ojo, sí que tindrem que alliberar-la.
|
|
surf->pixels = LoadGif(buffer, &surf->w, &surf->h);
|
|
|
|
// Com ja no ens fa falta, alliberem la memòria del buffer del arxiu
|
|
free(buffer);
|
|
|
|
// I finalment tornem la superficie
|
|
return surf;
|
|
}
|
|
|
|
void freeSurface(surface *surface)
|
|
{
|
|
// Si la superficie existeix...
|
|
if (surface != nullptr)
|
|
{
|
|
// Si el array de pixels existeix, l'alliberem
|
|
if (surface->pixels != nullptr)
|
|
{
|
|
free(surface->pixels);
|
|
}
|
|
|
|
// ... alliberem la superficie
|
|
free(surface);
|
|
}
|
|
}
|
|
|
|
void draw(surface *src, surface *dst, uint16_t src_offset, uint16_t w, uint16_t h, uint16_t dst_x, uint16_t dst_y)
|
|
{
|
|
if (!src) src = screen;
|
|
if (!dst) dst = screen;
|
|
const int src_x = src_offset % src->w;
|
|
const int src_y = src_offset / src->w;
|
|
for (int y=0; y<h; ++y)
|
|
for (int x=0; x<w; ++x)
|
|
{
|
|
uint8_t color = getPixel(src_x+x, src_y+y, src);
|
|
if (color != 0) putPixel(dst_x+x, dst_y+y, color , dst);
|
|
}
|
|
if (dst==screen) render();
|
|
}
|
|
|
|
void draw(surface *src, surface *dst)
|
|
{
|
|
if (!src) src = screen;
|
|
if (!dst) dst = screen;
|
|
const int w = src->w<dst->w ? src->w : dst->w;
|
|
const int h = src->h<dst->h ? src->h : dst->h;
|
|
for (int y=0;y<h;++y)
|
|
for (int x=0;x<w;++x)
|
|
dst->pixels[x+y*dst->w] = src->pixels[x+y*src->w];
|
|
if (dst==screen) render();
|
|
}
|
|
|
|
void cls(const uint8_t color, surface *dst)
|
|
{
|
|
if (!dst) dst = screen;
|
|
|
|
// El tamany es width x height bytes
|
|
const int size = dst->w * dst->h;
|
|
|
|
// Omplim la memòria dels pixels de la superficie de destinació amb "color"
|
|
memset(dst->pixels, color, size);
|
|
if (dst==screen) render();
|
|
}
|
|
|
|
// Estableix la paleta del sistema carregant-la d'un GIF
|
|
void loadPalette(const char *filename)
|
|
{
|
|
// Agafem un buffer de bytes de l'arxiu especificat
|
|
FILE *f = fopen(filename, "r");
|
|
if (f==nullptr) return;
|
|
fseek(f, 0, SEEK_END);
|
|
const int size = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
uint8_t *buffer = (uint8_t*)malloc(size);
|
|
fread(buffer, size, 1, f);
|
|
fclose(f);
|
|
|
|
// Li passem el array del arxiu a LoadPalette. Ell ens torna un array de uint32_t amb la paleta
|
|
// Van a ser 256 entrades de 32 bits, cada entrada es un color, amb el format 0xAARRGGBB
|
|
uint32_t *pal = LoadPalette(buffer);
|
|
|
|
// Copiem eixe array al nostre array de la paleta de sistema. Ara ja tenim la paleta carregada.
|
|
memcpy(palette, pal, 1024); // 32 bits per entrada == 4 bytes x 256 entrades == 1024
|
|
|
|
// Alliberem el array que ens habia tornat LoadPalette()
|
|
free(pal);
|
|
|
|
// I també el buffer del arxiu
|
|
free(buffer);
|
|
}
|
|
|
|
void setPalette(uint8_t *paleta)
|
|
{
|
|
uint8_t *p = paleta;
|
|
for (int i=0; i<256; ++i)
|
|
{
|
|
uint32_t color = (*p++)<<16;
|
|
color += (*p++)<<8;
|
|
color += (*p++);
|
|
palette[i] = color;
|
|
}
|
|
}
|
|
|
|
uint8_t *getPalette()
|
|
{
|
|
uint8_t *paleta = (uint8_t*)malloc(768);
|
|
uint8_t *p = paleta;
|
|
for (int i=0; i<256; ++i)
|
|
{
|
|
const uint32_t color = palette[i];
|
|
*p = (color >> 16) & 0xff; p++;
|
|
*p = (color >> 8) & 0xff; p++;
|
|
*p = color & 0xff; p++;
|
|
}
|
|
return paleta;
|
|
}
|
|
|
|
void setColor(const uint8_t index, const uint8_t r, const uint8_t g, const uint8_t b)
|
|
{
|
|
palette[index] = (uint32_t(r)<<16) + (uint32_t(g)<<8) + (uint32_t(b));
|
|
}
|
|
|
|
void getColor(const uint8_t index, uint8_t *r, uint8_t *g, uint8_t *b)
|
|
{
|
|
const uint32_t color = palette[index];
|
|
*r = (color >> 16) & 0xff;
|
|
*g = (color >> 8) & 0xff;
|
|
*b = color & 0xff;
|
|
}
|
|
|
|
void blackout()
|
|
{
|
|
for (int i=0; i<256; ++i) palette[i] = 0;
|
|
}
|
|
|
|
void fadeDown(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t espera) // espera son el nombre de waitVsync a executar en cada iteració del bucle
|
|
{
|
|
for (int i=0; i<256; ++i)
|
|
{
|
|
bool done = true;
|
|
for (int j=0; j<espera; ++j) waitVsync();
|
|
uint8_t rr, gg, bb;
|
|
for (int j=0; j<256; ++j)
|
|
{
|
|
getColor(j, &rr, &gg, &bb);
|
|
if (rr>r+4) { rr-=4; done = false; }
|
|
if (gg>g+4) { gg-=4; done = false; }
|
|
if (bb>b+4) { bb-=4; done = false; }
|
|
setColor(j, rr, gg, bb);
|
|
}
|
|
render();
|
|
if (done) return;
|
|
}
|
|
}
|
|
|
|
void fadeUp(const uint8_t *paleta, const uint8_t espera)
|
|
{
|
|
for (int i=0; i<256; ++i)
|
|
{
|
|
bool done = true;
|
|
for (int j=0; j<espera; ++j) waitVsync();
|
|
uint8_t r, g, b;
|
|
for (int j=0; j<256; ++j)
|
|
{
|
|
getColor(j, &r, &g, &b);
|
|
if (paleta[j*3]>r+4) { r+=4; done = false; } else { r=paleta[j*3]; }
|
|
if (paleta[j*3+1]>g+4) { g+=4; done = false; } else { g=paleta[j*3+1]; }
|
|
if (paleta[j*3+2]>b+4) { b+=4; done = false; } else { b=paleta[j*3+2]; }
|
|
setColor(j, r, g, b);
|
|
}
|
|
render();
|
|
if (done) return;
|
|
}
|
|
}
|
|
|
|
void putPixel(const int x, const int y, const uint8_t color, surface *dst)
|
|
{
|
|
if (!dst) dst = screen;
|
|
if (x<0 || y<0 || x>=dst->w || y>=dst->h) return;
|
|
dst->pixels[x+y*dst->w] = color;
|
|
}
|
|
|
|
uint8_t getPixel(const int x, const int y, surface *src)
|
|
{
|
|
if (!src) src = screen;
|
|
if (x<0 || y<0 || x>=src->w || y>=src->h) return 0;
|
|
return src->pixels[x+y*src->w];
|
|
}
|
|
|
|
void vline(const int x, const int y1, const int y2, const uint8_t color, surface *dst)
|
|
{
|
|
if (!dst) dst = screen;
|
|
for (int y=y1; y<=y2; ++y) dst->pixels[x+(y*dst->w)] = color;
|
|
}
|
|
|
|
void waitVsync()
|
|
{
|
|
const uint32_t next_vsync = SDL_GetTicks() + (17 - SDL_GetTicks()%17);
|
|
while (SDL_GetTicks() < next_vsync)
|
|
{
|
|
update();
|
|
}
|
|
}
|
|
|
|
void render()
|
|
{
|
|
Uint32 *sdl_pixels; // Punter al array de pixels que enstornarà SDL_LockTexture
|
|
int sdl_pitch; // Ací estarà guardat el pitch de la textura, com es de 32 bits, no m'afecta
|
|
const uint32_t size = screen->w * screen->h; // tamany de la superficie
|
|
|
|
// Bloquejem la textura SDL i agafem els seus pixels (son enters de 32 bits amb format 0xAARRGGBB)
|
|
SDL_LockTexture(sdl_texture, NULL, (void **)&sdl_pixels, &sdl_pitch);
|
|
|
|
// Cada pixel de la superficie "screen" es un enter de 8 bits que representa un index en la paleta de colors
|
|
// Per tant, per a pintar en la textura SDL, pillem el color de la paleta que correspon al index en "screen"
|
|
// i el enviem a la textura SDL
|
|
for (uint32_t i = 0; i < size; ++i)
|
|
{
|
|
sdl_pixels[i] = palette[screen->pixels[i]];
|
|
}
|
|
|
|
// Desbloquejem la textura
|
|
SDL_UnlockTexture(sdl_texture);
|
|
|
|
// Pintem la textura a pantalla
|
|
SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
|
|
|
|
// I ho presentem
|
|
SDL_RenderPresent(sdl_renderer);
|
|
}
|
|
}
|