libbpg-0.9.5
This commit is contained in:
parent
6e56352f86
commit
357f186837
35 changed files with 3022 additions and 2134 deletions
538
bpgview.c
Normal file
538
bpgview.c
Normal file
|
@ -0,0 +1,538 @@
|
|||
/*
|
||||
* BPG viewer
|
||||
*
|
||||
* Copyright (c) 2014-2015 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <SDL/SDL.h>
|
||||
#include <SDL/SDL_image.h>
|
||||
|
||||
#include "libbpg.h"
|
||||
|
||||
typedef enum {
|
||||
BG_BLACK,
|
||||
BG_TILED,
|
||||
} BackgroundTypeEnum;
|
||||
|
||||
typedef struct {
|
||||
SDL_Surface *img;
|
||||
int delay; /* in ms */
|
||||
} Frame;
|
||||
|
||||
typedef struct {
|
||||
int screen_w, screen_h;
|
||||
int win_w, win_h;
|
||||
SDL_Surface *screen;
|
||||
|
||||
int img_w, img_h;
|
||||
int frame_count;
|
||||
Frame *frames;
|
||||
int frame_index; /* index of the current frame */
|
||||
int loop_counter;
|
||||
int loop_count;
|
||||
SDL_TimerID frame_timer_id;
|
||||
|
||||
int is_full_screen;
|
||||
int pos_x, pos_y;
|
||||
BackgroundTypeEnum background_type;
|
||||
} DispContext;
|
||||
|
||||
static uint32_t timer_cb(uint32_t interval, void *param);
|
||||
|
||||
static inline int clamp_int(int val, int min_val, int max_val)
|
||||
{
|
||||
if (val < min_val)
|
||||
return min_val;
|
||||
else if (val > max_val)
|
||||
return max_val;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
|
||||
Frame *bpg_load(FILE *f, int *pframe_count, int *ploop_count)
|
||||
{
|
||||
BPGDecoderContext *s;
|
||||
BPGImageInfo bi_s, *bi = &bi_s;
|
||||
uint8_t *buf;
|
||||
int len, y;
|
||||
SDL_Surface *img;
|
||||
Frame *frames;
|
||||
uint32_t rmask, gmask, bmask, amask;
|
||||
int frame_count, i, delay_num, delay_den;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if (len < 0)
|
||||
return NULL;
|
||||
buf = malloc(len);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
if (fread(buf, 1, len, f) != len)
|
||||
return NULL;
|
||||
|
||||
frames = NULL;
|
||||
frame_count = 0;
|
||||
|
||||
s = bpg_decoder_open();
|
||||
if (bpg_decoder_decode(s, buf, len) < 0)
|
||||
goto fail;
|
||||
bpg_decoder_get_info(s, bi);
|
||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||
rmask = 0xff000000;
|
||||
gmask = 0x00ff0000;
|
||||
bmask = 0x0000ff00;
|
||||
amask = 0x000000ff;
|
||||
#else
|
||||
rmask = 0x000000ff;
|
||||
gmask = 0x0000ff00;
|
||||
bmask = 0x00ff0000;
|
||||
amask = 0xff000000;
|
||||
#endif
|
||||
for(;;) {
|
||||
if (bpg_decoder_start(s, BPG_OUTPUT_FORMAT_RGBA32) < 0)
|
||||
break;
|
||||
bpg_decoder_get_frame_duration(s, &delay_num, &delay_den);
|
||||
frames = realloc(frames, sizeof(frames[0]) * (frame_count + 1));
|
||||
img = SDL_CreateRGBSurface(SDL_HWSURFACE, bi->width, bi->height, 32,
|
||||
rmask, gmask, bmask, amask);
|
||||
if (!img)
|
||||
goto fail;
|
||||
|
||||
SDL_LockSurface(img);
|
||||
for(y = 0; y < bi->height; y++) {
|
||||
bpg_decoder_get_line(s, (uint8_t *)img->pixels + y * img->pitch);
|
||||
}
|
||||
SDL_UnlockSurface(img);
|
||||
frames[frame_count].img = img;
|
||||
frames[frame_count].delay = (delay_num * 1000) / delay_den;
|
||||
frame_count++;
|
||||
}
|
||||
bpg_decoder_close(s);
|
||||
*pframe_count = frame_count;
|
||||
*ploop_count = bi->loop_count;
|
||||
return frames;
|
||||
fail:
|
||||
bpg_decoder_close(s);
|
||||
for(i = 0; i < frame_count; i++) {
|
||||
SDL_FreeSurface(frames[i].img);
|
||||
}
|
||||
free(frames);
|
||||
*pframe_count = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void restart_frame_timer(DispContext *dc)
|
||||
{
|
||||
if (dc->frame_timer_id) {
|
||||
/* XXX: the SDL timer API is not safe, so we remove the timer even if it already expired */
|
||||
SDL_RemoveTimer(dc->frame_timer_id);
|
||||
dc->frame_timer_id = 0;
|
||||
}
|
||||
dc->frame_timer_id =
|
||||
SDL_AddTimer(dc->frames[dc->frame_index].delay, timer_cb, NULL);
|
||||
}
|
||||
|
||||
int load_image(DispContext *dc, const char *filename)
|
||||
{
|
||||
SDL_Surface *img;
|
||||
Frame *frames;
|
||||
FILE *f;
|
||||
uint8_t buf[BPG_DECODER_INFO_BUF_SIZE];
|
||||
int len, i, frame_count, loop_count;
|
||||
BPGImageInfo bi;
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
if (!f)
|
||||
goto fail;
|
||||
len = fread(buf, 1, sizeof(buf), f);
|
||||
if (bpg_decoder_get_info_from_buf(&bi, NULL, buf, len) >= 0) {
|
||||
fseek(f, 0, SEEK_SET);
|
||||
frames = bpg_load(f, &frame_count, &loop_count);
|
||||
if (!frames)
|
||||
goto fail;
|
||||
fclose(f);
|
||||
} else {
|
||||
/* use SDL image loader */
|
||||
img = IMG_Load(filename);
|
||||
if (!img) {
|
||||
fail:
|
||||
fprintf(stderr, "Could not load '%s'\n", filename);
|
||||
return -1;
|
||||
}
|
||||
frame_count = 1;
|
||||
frames = malloc(sizeof(dc->frames[0]) * frame_count);
|
||||
frames[0].img = img;
|
||||
frames[0].delay = 0;
|
||||
loop_count = 1;
|
||||
}
|
||||
|
||||
for(i = 0; i < dc->frame_count; i++) {
|
||||
SDL_FreeSurface(dc->frames[i].img);
|
||||
}
|
||||
free(dc->frames);
|
||||
if (dc->frame_timer_id) {
|
||||
SDL_RemoveTimer(dc->frame_timer_id);
|
||||
dc->frame_timer_id = 0;
|
||||
}
|
||||
|
||||
dc->frame_count = frame_count;
|
||||
dc->frames = frames;
|
||||
dc->frame_index = 0;
|
||||
dc->loop_counter = 0;
|
||||
dc->loop_count = loop_count;
|
||||
dc->img_w = dc->frames[0].img->w;
|
||||
dc->img_h = dc->frames[0].img->h;
|
||||
|
||||
/* start the animation timer if needed */
|
||||
if (dc->frame_count > 1) {
|
||||
restart_frame_timer(dc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void center_image(DispContext *dc)
|
||||
{
|
||||
dc->pos_x = clamp_int((dc->screen->w - dc->img_w) / 2, -32767, 32768);
|
||||
dc->pos_y = clamp_int((dc->screen->h - dc->img_h) / 2, -32767, 32768);
|
||||
}
|
||||
|
||||
void draw_image(DispContext *dc)
|
||||
{
|
||||
SDL_Rect r;
|
||||
|
||||
r.x = 0;
|
||||
r.y = 0;
|
||||
r.w = dc->screen->w;
|
||||
r.h = dc->screen->h;
|
||||
SDL_FillRect(dc->screen, &r, SDL_MapRGB(dc->screen->format, 0x00, 0x00, 0x00));
|
||||
|
||||
if (dc->background_type == BG_TILED) {
|
||||
int x, y, tw, w, h, x2, y2, w1, h1, x1, y1;
|
||||
uint32_t bgcolors[2];
|
||||
|
||||
tw = 16;
|
||||
w = dc->img_w;
|
||||
h = dc->img_h;
|
||||
w1 = (w + tw - 1) / tw;
|
||||
h1 = (h + tw - 1) / tw;
|
||||
bgcolors[0] = SDL_MapRGB(dc->screen->format, 100, 100, 100);
|
||||
bgcolors[1] = SDL_MapRGB(dc->screen->format, 150, 150, 150);
|
||||
for(y = 0; y < h1; y++) {
|
||||
for(x = 0; x < w1; x++) {
|
||||
x1 = x * tw;
|
||||
y1 = y * tw;
|
||||
x2 = x1 + tw;
|
||||
y2 = y1 + tw;
|
||||
if (x2 > w)
|
||||
x2 = w;
|
||||
if (y2 > h)
|
||||
y2 = h;
|
||||
r.x = x1 + dc->pos_x;
|
||||
r.y = y1 + dc->pos_y;
|
||||
r.w = x2 - x1;
|
||||
r.h = y2 - y1;
|
||||
SDL_FillRect(dc->screen, &r, bgcolors[(x ^ y) & 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.x = dc->pos_x;
|
||||
r.y = dc->pos_y;
|
||||
r.w = 0;
|
||||
r.h = 0;
|
||||
SDL_BlitSurface (dc->frames[dc->frame_index].img, NULL, dc->screen, &r);
|
||||
|
||||
SDL_Flip(dc->screen);
|
||||
}
|
||||
|
||||
void pan_image(DispContext *dc, int dx, int dy)
|
||||
{
|
||||
int dw, dh;
|
||||
|
||||
dw = dc->img_w - dc->screen->w;
|
||||
dh = dc->img_h - dc->screen->h;
|
||||
if (dw > 0) {
|
||||
dc->pos_x += dx;
|
||||
if (dc->pos_x < -dw)
|
||||
dc->pos_x = -dw;
|
||||
else if (dc->pos_x > 0)
|
||||
dc->pos_x = 0;
|
||||
}
|
||||
if (dh > 0) {
|
||||
dc->pos_y += dy;
|
||||
if (dc->pos_y < -dh)
|
||||
dc->pos_y = -dh;
|
||||
else if (dc->pos_y > 0)
|
||||
dc->pos_y = 0;
|
||||
}
|
||||
draw_image(dc);
|
||||
}
|
||||
|
||||
static void set_caption(DispContext *dc, char **argv,
|
||||
int image_index, int image_count)
|
||||
{
|
||||
char buf[1024];
|
||||
const char *filename;
|
||||
filename = argv[image_index];
|
||||
snprintf(buf, sizeof(buf), "bpgview [%d of %d] - %s",
|
||||
image_index + 1, image_count, filename);
|
||||
SDL_WM_SetCaption(buf, buf);
|
||||
}
|
||||
|
||||
static void open_window(DispContext *dc, int w, int h, int is_full_screen)
|
||||
{
|
||||
int flags;
|
||||
|
||||
flags = SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_HWACCEL;
|
||||
if (is_full_screen)
|
||||
flags |= SDL_FULLSCREEN;
|
||||
else
|
||||
flags |= SDL_RESIZABLE;
|
||||
|
||||
dc->screen = SDL_SetVideoMode(w, h, 32, flags);
|
||||
if (!dc->screen) {
|
||||
fprintf(stderr, "Could not init screen\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t timer_cb(uint32_t interval, void *param)
|
||||
{
|
||||
SDL_Event event;
|
||||
SDL_UserEvent userevent;
|
||||
|
||||
userevent.type = SDL_USEREVENT;
|
||||
userevent.code = 0;
|
||||
userevent.data1 = NULL;
|
||||
userevent.data2 = NULL;
|
||||
|
||||
event.type = SDL_USEREVENT;
|
||||
event.user = userevent;
|
||||
|
||||
SDL_PushEvent(&event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DEFAULT_W 640
|
||||
#define DEFAULT_H 480
|
||||
|
||||
static void help(void)
|
||||
{
|
||||
const char *str;
|
||||
str = "BPG Image Viewer version " CONFIG_BPG_VERSION "\n"
|
||||
"usage: bpgview infile...\n"
|
||||
"\n"
|
||||
"Keys:\n"
|
||||
"q, ESC quit\n"
|
||||
"n, SPACE next image\n"
|
||||
"p previous image\n"
|
||||
"arrows pan\n"
|
||||
"c center\n"
|
||||
"b toggle background type\n";
|
||||
#ifdef WIN32
|
||||
MessageBox(NULL, str, "Error", MB_ICONERROR | MB_OK);
|
||||
exit(1);
|
||||
#else
|
||||
printf("%s", str);
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int c, image_index, image_count, incr, i;
|
||||
SDL_Event event;
|
||||
DispContext dc_s, *dc = &dc_s;
|
||||
const SDL_VideoInfo *vi;
|
||||
|
||||
for(;;) {
|
||||
c = getopt(argc, argv, "h");
|
||||
if (c == -1)
|
||||
break;
|
||||
switch(c) {
|
||||
case 'h':
|
||||
show_help:
|
||||
help();
|
||||
break;
|
||||
default:
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc)
|
||||
goto show_help;
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
|
||||
fprintf(stderr, "Could not init SDL\n");
|
||||
exit(1);
|
||||
}
|
||||
memset(dc, 0, sizeof(*dc));
|
||||
|
||||
vi = SDL_GetVideoInfo();
|
||||
dc->screen_w = vi->current_w;
|
||||
dc->screen_h = vi->current_h;
|
||||
dc->is_full_screen = 0;
|
||||
|
||||
image_count = argc - optind;
|
||||
image_index = 0;
|
||||
if (load_image(dc, argv[optind + image_index]) < 0)
|
||||
exit(1);
|
||||
dc->background_type = BG_TILED;
|
||||
|
||||
{
|
||||
int w, h;
|
||||
|
||||
if (image_count > 1 || (dc->img_w < 256 || dc->img_h < 256)) {
|
||||
w = DEFAULT_W;
|
||||
h = DEFAULT_H;
|
||||
} else {
|
||||
w = clamp_int(dc->img_w, 32, dc->screen_w);
|
||||
h = clamp_int(dc->img_h, 32, dc->screen_h);
|
||||
}
|
||||
open_window(dc, w, h, 0);
|
||||
set_caption(dc, argv + optind, image_index, image_count);
|
||||
}
|
||||
|
||||
center_image(dc);
|
||||
draw_image(dc);
|
||||
|
||||
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
|
||||
|
||||
for(;;) {
|
||||
if (!SDL_WaitEvent(&event))
|
||||
continue;
|
||||
switch(event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_ESCAPE:
|
||||
case SDLK_q:
|
||||
goto done;
|
||||
case SDLK_SPACE: /* next image */
|
||||
case SDLK_n:
|
||||
incr = 1;
|
||||
goto prev_next;
|
||||
case SDLK_p: /* previous image */
|
||||
incr = -1;
|
||||
prev_next:
|
||||
if (image_count > 1) {
|
||||
for(i = 0; i < image_count; i++) {
|
||||
image_index += incr;
|
||||
if (image_index < 0)
|
||||
image_index = image_count - 1;
|
||||
else if (image_index >= image_count)
|
||||
image_index = 0;
|
||||
if (load_image(dc, argv[optind + image_index]) == 0)
|
||||
break;
|
||||
}
|
||||
if (i == image_count)
|
||||
exit(1);
|
||||
set_caption(dc, argv + optind, image_index, image_count);
|
||||
center_image(dc);
|
||||
draw_image(dc);
|
||||
}
|
||||
break;
|
||||
case SDLK_LEFT:
|
||||
pan_image(dc, 32, 0);
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
pan_image(dc, -32, 0);
|
||||
break;
|
||||
case SDLK_UP:
|
||||
pan_image(dc, 0, 32);
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
pan_image(dc, 0, -32);
|
||||
break;
|
||||
case SDLK_c:
|
||||
center_image(dc);
|
||||
draw_image(dc);
|
||||
break;
|
||||
case SDLK_b:
|
||||
dc->background_type ^= 1;
|
||||
draw_image(dc);
|
||||
break;
|
||||
case SDLK_f:
|
||||
dc->is_full_screen ^= 1;
|
||||
if (dc->is_full_screen) {
|
||||
/* save old windows size */
|
||||
dc->win_w = dc->screen->w;
|
||||
dc->win_h = dc->screen->h;
|
||||
open_window(dc, dc->screen_w, dc->screen_h, 1);
|
||||
} else {
|
||||
open_window(dc, dc->win_w, dc->win_h, 0);
|
||||
}
|
||||
center_image(dc);
|
||||
draw_image(dc);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_VIDEORESIZE:
|
||||
{
|
||||
open_window(dc, event.resize.w, event.resize.h, 0);
|
||||
center_image(dc);
|
||||
draw_image(dc);
|
||||
}
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
goto done;
|
||||
case SDL_MOUSEMOTION:
|
||||
if (event.motion.state) {
|
||||
pan_image(dc, event.motion.xrel, event.motion.yrel);
|
||||
}
|
||||
break;
|
||||
case SDL_USEREVENT:
|
||||
if (dc->frame_count > 1) {
|
||||
/* show next frame */
|
||||
if (dc->frame_index == (dc->frame_count - 1)) {
|
||||
if (dc->loop_count == 0 ||
|
||||
dc->loop_counter < (dc->loop_count - 1)) {
|
||||
dc->frame_index = 0;
|
||||
dc->loop_counter++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
dc->frame_index++;
|
||||
}
|
||||
draw_image(dc);
|
||||
restart_frame_timer(dc);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
done:
|
||||
|
||||
SDL_FreeSurface(dc->screen);
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue