/* * gui.c - Graphical output for ubb-la * * Written 2013 by Werner Almesberger * Copyright 2013 Werner Almesberger * * 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. */ #include #include #include "SDL.h" #include "SDL_gfxPrimitives.h" #include "gui.h" #define XRES 320 /* canvas width */ #define YRES 240 /* canvas height */ #define LEVEL_RGBA 0xffff00ff #define BOUNCE_RGBA 0xff8080ff #define CH_XOFF 30 #define CH_YOFF 30 #define CH_SKIP 16 #define CH_HEIGHT 8 #define MAX_ZOOM 3 #define REPEAT_DELAY_MS 300 #define REPEAT_INTERVAL_MS 30 static SDL_Surface *surf; void gui_init(void) { if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "SDL_init: %s\n", SDL_GetError()); exit(1); } atexit(SDL_Quit); surf = SDL_SetVideoMode(XRES, YRES, 0, SDL_SWSURFACE); if (!surf) { fprintf(stderr, "SDL_SetVideoMode: %s\n", SDL_GetError()); exit(1); } SDL_EnableKeyRepeat(REPEAT_DELAY_MS, REPEAT_INTERVAL_MS); } static void clear(void) { SDL_LockSurface(surf); SDL_FillRect(surf, NULL, SDL_MapRGB(surf->format, 0, 0, 0)); } static inline int ch_y(int ch, int v) { return CH_YOFF+CH_SKIP*ch+(v ? 0 : CH_HEIGHT); } static void bounce(int x, int ch) { vlineColor(surf, x, ch_y(ch, 0), ch_y(ch, 1), BOUNCE_RGBA); } static void change(int x, int ch) { vlineColor(surf, x, ch_y(ch, 0), ch_y(ch, 1), LEVEL_RGBA); } static void level(int x0, int x1, int ch, int v) { if (x0 == x1) pixelColor(surf, x0, ch_y(ch, v), LEVEL_RGBA); else hlineColor(surf, x0, x1, ch_y(ch, v), LEVEL_RGBA); } static void show_buffer_zoom_in(const uint8_t *buf, int skip, int nibbles, int x0, int x1) { int f = (x1-x0)/(nibbles-skip); int x, i, j; int last[4] = { -1, -1, -1, -1}; uint8_t v, bit; fprintf(stderr, "in: %d-%d (%d) Sa %d-%d (%d) pix %d f (%d)\n", skip, nibbles, nibbles-skip, x0, x1, x1-x0, f, f*(nibbles-skip)); x = x0; for (i = skip; i != nibbles; i++) { v = (buf[i >> 1] >> 4*(~i & 1)) & 0xf; for (j = 0; j != 4; j++) { bit = (v >> j) & 1; if (bit == last[j] || last[j] == -1) { level(x, x+f-1, j, bit); } else { change(x, j); level(x+1, x+f-1, j, bit); } last[j] = bit; } x += f; } } static void show_buffer_zoom_out(const uint8_t *buf, int skip, int nibbles, int x0, int x1) { int f = (nibbles-skip)/(x1-x0); int x; int i = skip, n, j; int changes[4], last[4] = { -1, -1, -1, -1}, next[4]; uint8_t v, bit; fprintf(stderr, "out: %d-%d (%d) Sa %d-%d (%d) pix %d f (%d)\n", skip, nibbles, nibbles-skip, x0, x1, x1-x0, f, f*(x1-x0)); for (x = x0; x != x1; x++) { n = i+f; for (j = 0; j != 4; j++) { next[j] = last[j]; changes[j] = 0; } while (i != n) { v = (buf[i >> 1] >> 4*(~i & 1)) & 0xf; for (j = 0; j != 4; j++) { bit = (v >> j) & 1; if (bit != next[j]) { changes[j]++; next[j] = bit; } } i++; } for (j = 0; j != 4; j++) { if (changes[j] > 1) bounce(x, j); else if (!changes[j] || last[j] == -1) level(x, x, j, next[j]); else change(x, j); last[j] = next[j]; } } } static void show_buffer(const uint8_t *buf, int skip, int nibbles, int x0, int x1, int zoom, int pos) { int xm, w, p0, p1; int d, dp; xm = (x0+x1) >> 1; dp = pos-((nibbles+skip) >> 1); if (zoom < 0) { w = (nibbles-skip) >> -zoom; p0 = xm-(w >> 1)-(dp >> -zoom); p1 = xm+((w+1) >> 1)-(dp >> -zoom); if (p0 < x0) { skip += (x0-p0) << -zoom; p0 = x0; } if (p1 > x1) { nibbles -= (p1-x1) << -zoom; p1 = x1; } show_buffer_zoom_out(buf, skip, nibbles, p0, p1); } else { w = (nibbles-skip) << zoom; p0 = xm-(w >> 1)-(dp << zoom); p1 = xm+((w+1) >> 1)-(dp << zoom); if (p0 < x0) { d = ((x0-p0)+(1 << zoom)-1) >> zoom; skip += d; p0 += d << zoom; } if (p1 > x1) { d = ((p1-x1)+(1 << zoom)-1) >> zoom; nibbles -= d; p1 -= d << zoom; } show_buffer_zoom_in(buf, skip, nibbles, p0, p1); } } static void update(void) { SDL_UnlockSurface(surf); SDL_UpdateRect(surf, 0, 0, 0, 0); } void gui(const uint8_t *buf, int skip, int nibbles) { SDL_Event event; int pos = (skip+nibbles) >> 1; int zoom; /* < 0: zoom out; 0: 1 pixel = 1 sample; > 1: zoom in */ int min_zoom = 0; while (XRES-CH_XOFF < (nibbles-skip) >> -min_zoom) min_zoom--; zoom = min_zoom; while (1) { clear(); show_buffer(buf, skip, nibbles, CH_XOFF, XRES, zoom, pos); update(); while (1) { SDL_WaitEvent(&event); switch (event.type) { case SDL_KEYDOWN: switch (event.key.keysym.sym) { case SDLK_UP: if (zoom < MAX_ZOOM) zoom++; break; case SDLK_DOWN: if (zoom > min_zoom) zoom--; break; case SDLK_LEFT: if (pos) pos--; break; case SDLK_RIGHT: if (pos < nibbles-1) pos++; break; case SDLK_RETURN: case SDLK_q: return; default: printf("%x\n", event.key.keysym.sym); continue; } break; case SDL_QUIT: return; default: continue; } break; } } }