mivfx/format/gif.c

163 行
4.2 KiB
C

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