TODO: GIF対応
このコミットが含まれているのは:
コミット
8705f33822
4
Makefile
4
Makefile
|
@ -3,9 +3,9 @@ VERSION=0.1.0
|
|||
# Linux、Haiku、かIllumos = /usr、FreeBSDかOpenBSD = /usr/local、NetBSD = /usr/pkg
|
||||
PREFIX=/usr
|
||||
CC=cc
|
||||
FILES=main.c format/png.c format/webp.c
|
||||
FILES=main.c format/png.c format/webp.c format/gif.c
|
||||
CFLAGS=-Wall -Wextra -g
|
||||
LDFLAGS=-lX11 -lpng -lwebp
|
||||
LDFLAGS=-lX11 -lpng -lwebp -lgif
|
||||
|
||||
all:
|
||||
${CC} ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS}
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
#include "gif.h"
|
||||
|
||||
XImage *ximage_from_frame(Display *d, GifFileType *file) {
|
||||
int depth = 32;
|
||||
int width = file->Image.Width;
|
||||
int height = file->Image.Height;
|
||||
|
||||
char *imgdata = malloc(width * height * 4);
|
||||
if (imgdata == NULL) {
|
||||
perror("画像データ向けメモリを割り当てに失敗\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GifPixelType *raster = (GifPixelType *)malloc(width * height * sizeof(GifPixelType));
|
||||
if (!raster) {
|
||||
fprintf(stderr, "メモリ割当に失敗\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < height; ++i) {
|
||||
if (DGifGetLine(file, raster + i * width, width) == GIF_ERROR) {
|
||||
fprintf(stderr, "ラスタースキャンに失敗\n");
|
||||
free(raster);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ColorMapObject *cmap = file->Image.ColorMap ? file->Image.ColorMap : file->SColorMap;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
GifPixelType px = raster[y * width + x];
|
||||
GifColorType col = cmap->Colors[px];
|
||||
|
||||
// 32ビットARGB形式に交換
|
||||
uint32_t argb = 0xFF << 24 | // 透明
|
||||
col.Red << 16 | // 赤
|
||||
col.Green << 8 | // 緑
|
||||
col.Blue; // 青
|
||||
|
||||
// imgdataに保存
|
||||
((uint32_t *)imgdata)[y * width + x] = argb;
|
||||
}
|
||||
}
|
||||
|
||||
free(raster);
|
||||
|
||||
// XImageを創作
|
||||
XImage *img = XCreateImage(d, DefaultVisual(d, 0), depth, ZPixmap, 0, imgdata, width, height, 32, 0);
|
||||
if (img == NULL) {
|
||||
fprintf(stderr, "XImageを創作に失敗\n");
|
||||
free(imgdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
GifAnimation* read_gif(Display *d, const char *filename) {
|
||||
GifFileType *file;
|
||||
if ((file = DGifOpenFileName(filename, NULL)) == NULL) {
|
||||
perror("GIFファイルを開けられません。");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GifAnimation *anime = malloc(sizeof(GifAnimation));
|
||||
if (anime == NULL) {
|
||||
DGifCloseFile(file, NULL);
|
||||
perror("GIFファイルを開けられません。");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int framecnt = 0;
|
||||
int ExtCode;
|
||||
GifByteType *Extension;
|
||||
GifRecordType RecordType;
|
||||
|
||||
do {
|
||||
if (DGifGetRecordType(file, &RecordType) == GIF_ERROR) {
|
||||
fprintf(stderr, "GIFエラー\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (RecordType == IMAGE_DESC_RECORD_TYPE) {
|
||||
framecnt++;
|
||||
}
|
||||
} while (RecordType != TERMINATE_RECORD_TYPE);
|
||||
|
||||
anime->frames = malloc(sizeof(XImage*) * framecnt);
|
||||
anime->delays = malloc(sizeof(int) * framecnt);
|
||||
|
||||
if (anime->frames == NULL || anime->delays == NULL) {
|
||||
fprintf(stderr, "フレームや延長向けメモリを作成に失敗しました。\n");
|
||||
free(anime);
|
||||
DGifCloseFile(file, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DGifCloseFile(file, NULL);
|
||||
file = DGifOpenFileName(filename, NULL);
|
||||
|
||||
int i = 0;
|
||||
do {
|
||||
if (DGifGetRecordType(file, &RecordType) == GIF_ERROR) {
|
||||
fprintf(stderr, "GIFエラー\n");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (RecordType) {
|
||||
case IMAGE_DESC_RECORD_TYPE:
|
||||
if (DGifGetImageDesc(file) == GIF_ERROR) {
|
||||
fprintf(stderr, "GIF説明エラー\n");
|
||||
break;
|
||||
}
|
||||
|
||||
XImage *img = ximage_from_frame(d, file);
|
||||
if (img == NULL) {
|
||||
fprintf(stderr, "フレーム %d のXImageを創作に失敗。\n", i);
|
||||
break;
|
||||
}
|
||||
|
||||
anime->frames[i] = img;
|
||||
|
||||
i++;
|
||||
break;
|
||||
case EXTENSION_RECORD_TYPE:
|
||||
if (DGifGetExtension(file, &ExtCode, &Extension) == GIF_ERROR) {
|
||||
fprintf(stderr, "GIF種類エラー\n");
|
||||
}
|
||||
|
||||
if (ExtCode == GRAPHICS_EXT_FUNC_CODE) {
|
||||
int delay = Extension[2] | (Extension[3] << 8);
|
||||
anime->delays[i] = delay;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while (RecordType != TERMINATE_RECORD_TYPE);
|
||||
|
||||
if (framecnt == 0) {
|
||||
fprintf(stderr, "GIFフレームを見つけられませんでした。\n");
|
||||
free(anime);
|
||||
DGifCloseFile(file, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
anime->frames = malloc(sizeof(XImage*) * framecnt);
|
||||
anime->delays = malloc(sizeof(int) * framecnt);
|
||||
|
||||
if (anime->frames == NULL || anime->delays == NULL) {
|
||||
fprintf(stderr, "フレームや延長向けメモリを作成に失敗しました。\n");
|
||||
free(anime->frames);
|
||||
free(anime->delays);
|
||||
free(anime);
|
||||
DGifCloseFile(file, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DGifCloseFile(file, NULL);
|
||||
return anime;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef _GIF_H
|
||||
#define _GIF_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <gif_lib.h>
|
||||
|
||||
typedef struct {
|
||||
XImage **frames;
|
||||
int width;
|
||||
int height;
|
||||
int *delays;
|
||||
int framecnt;
|
||||
} GifAnimation;
|
||||
|
||||
typedef struct {
|
||||
GifAnimation *anime;
|
||||
int width;
|
||||
int height;
|
||||
XImage **frames;
|
||||
int framecnt;
|
||||
} GifWrap;
|
||||
|
||||
GifAnimation* read_gif(Display *d, const char *filename);
|
||||
|
||||
#endif
|
75
main.c
75
main.c
|
@ -2,6 +2,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
@ -9,6 +10,22 @@
|
|||
|
||||
#include "format/png.h"
|
||||
#include "format/webp.h"
|
||||
#include "format/gif.h"
|
||||
|
||||
typedef enum {
|
||||
STATIC,
|
||||
ANIME
|
||||
} ImgType;
|
||||
|
||||
typedef struct {
|
||||
ImgType type;
|
||||
int width;
|
||||
int height;
|
||||
union {
|
||||
XImage *simg;
|
||||
GifWrap *aimg;
|
||||
} imgdata;
|
||||
} LoliImg;
|
||||
|
||||
int XErrorHandlerd(Display *d, XErrorEvent *event) {
|
||||
char error_text[120];
|
||||
|
@ -17,7 +34,7 @@ int XErrorHandlerd(Display *d, XErrorEvent *event) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
XImage* openimg(Display *d, const char *filename) {
|
||||
LoliImg* openimg(Display *d, const char *filename) {
|
||||
FILE *fp;
|
||||
fp = fopen(filename, "rb");
|
||||
if (!fp) {
|
||||
|
@ -29,16 +46,54 @@ XImage* openimg(Display *d, const char *filename) {
|
|||
fread(buf, 1, 16, fp);
|
||||
fclose(fp);
|
||||
|
||||
LoliImg *limg = malloc(sizeof(LoliImg));
|
||||
|
||||
if (memcmp(buf, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0) { // PNG
|
||||
return read_png(d, filename);
|
||||
limg->type = STATIC;
|
||||
limg->imgdata.simg = read_png(d, filename);
|
||||
limg->width = limg->imgdata.simg->width;
|
||||
limg->height = limg->imgdata.simg->height;
|
||||
return limg;
|
||||
} else if (memcmp(buf + 8, "WEBP", 4) == 0) { // WEBP
|
||||
return read_webp(d, filename);
|
||||
limg->type = STATIC;
|
||||
limg->imgdata.simg = read_webp(d, filename);
|
||||
limg->width = limg->imgdata.simg->width;
|
||||
limg->height = limg->imgdata.simg->height;
|
||||
return limg;
|
||||
} else if (memcmp(buf, "GIF", 3) == 0) { // GIF
|
||||
GifWrap *wrap = malloc(sizeof(GifWrap));
|
||||
wrap->anime = read_gif(d, filename);
|
||||
wrap->width = wrap->anime->width;
|
||||
wrap->height = wrap->anime->height;
|
||||
wrap->frames = wrap->anime->frames;
|
||||
wrap->framecnt = wrap->anime->framecnt;
|
||||
|
||||
limg->type = ANIME;
|
||||
limg->imgdata.aimg = wrap;
|
||||
limg->width = wrap->width;
|
||||
limg->height = wrap->height;
|
||||
return limg;
|
||||
}
|
||||
|
||||
free(limg);
|
||||
fprintf(stderr, "不明なファイル種類。\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void GentleDestroyLoliImage(LoliImg *limg) {
|
||||
if (limg->type == STATIC) {
|
||||
XDestroyImage(limg->imgdata.simg);
|
||||
} else if (limg->type == ANIME) {
|
||||
GifWrap *wrap = limg->imgdata.aimg;
|
||||
// 各フレームを破壊する
|
||||
for (int i = 0; i < wrap->framecnt; ++i) {
|
||||
XDestroyImage(wrap->frames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(limg);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
XSetErrorHandler(XErrorHandlerd);
|
||||
if (argc < 2) {
|
||||
|
@ -64,7 +119,7 @@ int main(int argc, char **argv) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
XImage *ximg = openimg(d, argv[1]);
|
||||
LoliImg *ximg = openimg(d, argv[1]);
|
||||
if (ximg == NULL) {
|
||||
fprintf(stderr, "画像を開けられません: %s\n", argv[1]);
|
||||
XFreeGC(d, gc);
|
||||
|
@ -73,6 +128,9 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
|
||||
double scale = 1.0;
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
unsigned long curframe = 1000000 * tv.tv_sec + tv.tv_usec;
|
||||
|
||||
while (1) {
|
||||
XEvent e;
|
||||
|
@ -81,7 +139,12 @@ int main(int argc, char **argv) {
|
|||
if (e.type == Expose) {
|
||||
int nw = (int)(ximg->width * scale);
|
||||
int nh = (int)(ximg->height * scale);
|
||||
XPutImage(d, w, gc, ximg, 0, 0, 0, 0, nw, nh);
|
||||
if (ximg->type == STATIC) {
|
||||
XPutImage(d, w, gc, ximg->imgdata.simg, 0, 0, 0, 0, nw, nh);
|
||||
} else {
|
||||
GifWrap *wrap = ximg->imgdata.aimg;
|
||||
XPutImage(d, w, gc, wrap->frames[curframe], 0, 0, 0, 0, nw, nh);
|
||||
}
|
||||
}
|
||||
|
||||
if (e.type == KeyPress) {
|
||||
|
@ -98,7 +161,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
// 掃除
|
||||
XFreeGC(d, gc);
|
||||
XDestroyImage(ximg);
|
||||
GentleDestroyLoliImage(ximg);
|
||||
XDestroyWindow(d, w);
|
||||
XCloseDisplay(d);
|
||||
|
||||
|
|
読み込み中…
新しいイシューから参照