aboutsummaryrefslogtreecommitdiff
path: root/Src/gif/GIFLoader.cpp
blob: 5d2c858ea3dd6d149c8d3b9331f2bd37e6086727 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include "GIFLoader.h"
#include "api__gif.h"
#include <wchar.h>
#include <bfc/platform/strcmp.h>

extern "C" {
#include "../giflib/gif_lib.h"
};

static bool StringEnds(const wchar_t *a, const wchar_t *b)
{
	size_t aLen = wcslen(a);
	size_t bLen	= wcslen(b);
	if (aLen < bLen) return false;  // too short
	return !_wcsicmp(a + aLen - bLen, b);
}

int GIFLoader::isMine(const wchar_t *filename) 
{
	return (filename && StringEnds(filename, L".GIF"));
}

int GIFLoader::getHeaderSize() 
{
	return 3;
}

const wchar_t *GIFLoader::mimeType() 
{
	return L"image/gif";
}

int GIFLoader::testData(const void *data, int datalen)
{
	if(datalen >= 3 && strncmp((const char*)data,"GIF",3)==0) return 1;
	return 0;
}

typedef struct {
	BYTE * data;
	int len;
} readStruct;

extern "C" int myreader(GifFileType *gif, GifByteType *data, int len) {
	readStruct* r = (readStruct*)gif->UserData;
	int l = min(len,r->len);
	if(l == 0) return 0;
	memcpy(data,r->data,l);
	r->len -= l;
	r->data += l;
	return l;
}

ARGB32 *GIFLoader::loadImage(const void *datain, int datalen, int *w, int *h, ifc_xmlreaderparams *params)
{
	readStruct read = {(BYTE*)datain,datalen};
	int* l_error = NULL;
	GifFileType * gif = DGifOpen(&read, myreader, l_error);
	if(!gif) return 0;

	if(!DGifSlurp(gif) || !gif->ImageCount) { DGifCloseFile(gif, l_error); return 0; }

	ColorMapObject *map = gif->SColorMap;
	if(!map) map = gif->SavedImages[0].ImageDesc.ColorMap;
	if(!map) { DGifCloseFile(gif, l_error); return 0; }
	int iw = gif->SavedImages[0].ImageDesc.Width;
	int ih = gif->SavedImages[0].ImageDesc.Height;
	if(w) *w = iw;
	if(h) *h = ih;

	ARGB32 * data = (ARGB32 *)WASABI_API_MEMMGR->sysMalloc(iw * ih * sizeof(ARGB32));
	ARGB32 * p = data;
	ARGB32 * end = data + (iw * ih);
	
	GifPixelType *line = gif->SavedImages[0].RasterBits;


	if(gif->SavedImages[0].ImageDesc.Interlace)
	{
		// The way Interlaced image should be read - offsets and jumps... 
		const int InterlacedOffset[] = { 0, 4, 2, 1 };
		const int InterlacedJumps[] = { 8, 8, 4, 2 }; 

		for(int i = 0; i < 4; i++)
		{
			for(int j = InterlacedOffset[i]; j < ih; j += InterlacedJumps[i])
			{
				p = &data[j*iw];
				for(int k = 0; k<iw; k++)
				{
					int px = *(line++);
					if(px < map->ColorCount && (px != gif->SBackGroundColor || px == 0)) {
						GifColorType& color = map->Colors[px];
						*(p++) = 0xff000000 | color.Blue | (color.Green << 8) | (color.Red << 16);
					} else
						*(p++) = 0;
				}
			}
		}
	}
	else
	{
		while(p < end) {
			int px = *(line++);
			if(px < map->ColorCount && (px != gif->SBackGroundColor || px == 0)) {
				GifColorType& color = map->Colors[px];
				*(p++) = 0xff000000 | color.Blue | (color.Green << 8) | (color.Red << 16);
			} else
				*(p++) = 0;
		}
	}

	DGifCloseFile(gif, l_error);
	return data;
}

#define CBCLASS GIFLoader
START_DISPATCH;
  CB(ISMINE, isMine);
  CB(MIMETYPE, mimeType);
  CB(TESTDATA, testData);
  CB(GETHEADERSIZE, getHeaderSize);
  CB(GETDIMENSIONS, getDimensions);
  CB(LOADIMAGE, loadImage);
  CB(LOADIMAGEDATA, loadImageData);
END_DISPATCH;
#undef CBCLASS