TODO: GIF対応

このコミットが含まれているのは:
守矢諏訪子 2023-10-25 02:37:31 +09:00
コミット 8705f33822
4個のファイルの変更261行の追加8行の削除

ファイルの表示

@ -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}

162
format/gif.c ノーマルファイル
ファイルの表示

@ -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;
}

28
format/gif.h ノーマルファイル
ファイルの表示

@ -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
ファイルの表示

@ -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);