1 |
/* |
2 |
* PearPC |
3 |
* font.cc |
4 |
* |
5 |
* Copyright (C) 1999-2003 Sebastian Biallas (sb@biallas.net) |
6 |
* |
7 |
* This program is free software; you can redistribute it and/or modify |
8 |
* it under the terms of the GNU General Public License version 2 as |
9 |
* published by the Free Software Foundation. |
10 |
* |
11 |
* This program is distributed in the hope that it will be useful, |
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
* GNU General Public License for more details. |
15 |
* |
16 |
* You should have received a copy of the GNU General Public License |
17 |
* along with this program; if not, write to the Free Software |
18 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
19 |
*/ |
20 |
|
21 |
#include <cstring> |
22 |
|
23 |
#include "system/types.h" |
24 |
#include "system/gif.h" |
25 |
#include "tools/except.h" |
26 |
#include "tools/snprintf.h" |
27 |
|
28 |
struct GIF_IDB { |
29 |
byte ISH; // = 0x2c |
30 |
uint16 x; |
31 |
uint16 y; |
32 |
uint16 width; |
33 |
uint16 height; |
34 |
byte flags; |
35 |
byte initial_code_size; |
36 |
} PACKED; |
37 |
|
38 |
Gif::Gif() |
39 |
{ |
40 |
pic = NULL; |
41 |
} |
42 |
|
43 |
Gif::~Gif() |
44 |
{ |
45 |
delete[] pic; |
46 |
} |
47 |
|
48 |
Gif::Gif(Stream &str) |
49 |
{ |
50 |
pic = NULL; |
51 |
if (!loadFromByteStream(str)) { |
52 |
String res; str.getDesc(res); |
53 |
throw MsgfException("error loading '%y' (not a gif?)", &res); |
54 |
} |
55 |
} |
56 |
|
57 |
static inline bool getlogbyte(Stream &stream, int width, int &bitleft, int &byteleft, uint16 &lbyte, uint16 &curcode, int &bidx, byte *buf) |
58 |
{ |
59 |
curcode = lbyte >> (8-bitleft); |
60 |
curcode &= (1<<width)-1; |
61 |
int want = width - bitleft; |
62 |
int have = bitleft; |
63 |
bitleft -= width; |
64 |
if (bitleft<0) bitleft = 0; |
65 |
while (want > 0) { |
66 |
if (!byteleft) { |
67 |
stream.readx(buf, 1); |
68 |
byteleft = buf[0]; |
69 |
if (!byteleft) return false; |
70 |
stream.readx(buf, byteleft); |
71 |
bidx = 0; |
72 |
} |
73 |
byteleft--; |
74 |
lbyte = buf[bidx++]; |
75 |
bitleft+=8; |
76 |
int take = want; |
77 |
if (take > 8) take = 8; |
78 |
int a = lbyte & ((1<<take)-1); |
79 |
a <<= have; |
80 |
curcode |= a; |
81 |
bitleft -= take; |
82 |
have += take; |
83 |
want -= take; |
84 |
} |
85 |
return true; |
86 |
} |
87 |
|
88 |
bool Gif::loadFromByteStream(Stream &stream) |
89 |
{ |
90 |
byte buf[768]; |
91 |
int idx; |
92 |
uint16 alphStack[4096]; |
93 |
uint16 alphPrefix[4096]; |
94 |
uint16 alphTail[4096]; |
95 |
memset(alphStack, 0xff, sizeof alphStack); |
96 |
memset(alphPrefix, 0xff, sizeof alphPrefix); |
97 |
memset(alphTail, 0xff, sizeof alphTail); |
98 |
|
99 |
int free, width, max, stackp, bitleft, byteleft, bidx; |
100 |
uint16 curcode, oldcode, readb, lbyte, special; |
101 |
oldcode = 0; |
102 |
special = 0; |
103 |
|
104 |
stream.readx(buf, 6); // magic |
105 |
if (buf[0] != 'G' || buf[1] != 'I' || buf[2] != 'F' || buf[3] != '8' || buf[5] != 'a') return false; |
106 |
int version; |
107 |
switch (buf[4]) { |
108 |
case '7': version = 7; break; |
109 |
case '9': version = 9; break; |
110 |
default: return false; |
111 |
} |
112 |
|
113 |
stream.readx(buf, 7); // header |
114 |
|
115 |
mWidth = buf[0] + (buf[1]<<8); |
116 |
mHeight = buf[2] + (buf[3]<<8); |
117 |
if (buf[4] & 0x80) { |
118 |
// global color table |
119 |
int palsize = 3*(1<<((buf[4] & 7)+1)); |
120 |
stream.readx(mPal, palsize); |
121 |
} |
122 |
|
123 |
stream.readx(buf, 1); |
124 |
while (buf[0] == 0x21) { |
125 |
// skip extension blocks |
126 |
stream.readx(buf, 2); |
127 |
stream.readx(buf, buf[1]+1); |
128 |
stream.readx(buf, 1); |
129 |
} |
130 |
stream.readx(buf+1, 10); |
131 |
GIF_IDB *i = (GIF_IDB*)&buf; |
132 |
/* printf("IDB: ISH=%02x, x=%04x, y=%04x, width=%04x, height=%04x, flags=%02x\n", |
133 |
i->ISH, i->x, i->y, i->width, i->height, i->flags |
134 |
);*/ |
135 |
|
136 |
// image descriptor? |
137 |
if (i->ISH != 0x2c) return false; |
138 |
|
139 |
if (i->flags & 128) { |
140 |
// local color table |
141 |
int palsize = 3*(1<<((buf[8] & 7)+1)); |
142 |
stream.readx(mPal, palsize); |
143 |
} |
144 |
|
145 |
int initial_code_size = i->initial_code_size; |
146 |
int _CLR = 1<<initial_code_size; |
147 |
int _EOF = _CLR+1; |
148 |
lbyte = 0; |
149 |
free = _EOF+1; |
150 |
width = initial_code_size+1; |
151 |
max = (1<<width)-1; |
152 |
// ht_printf("i: %d c: %d e: %d max: %d, free: %d\n", initial_code_size, _CLR, _EOF, max, free); |
153 |
stackp = 0; |
154 |
bitleft = 0; |
155 |
byteleft = 0; |
156 |
idx = 0; |
157 |
|
158 |
pic = new byte[mWidth*mHeight]; |
159 |
|
160 |
while (true) { |
161 |
if (!getlogbyte(stream, width, bitleft, byteleft, lbyte, curcode, bidx, buf)) return false; |
162 |
g: |
163 |
if (curcode == _EOF) break; |
164 |
if (curcode == _CLR) { |
165 |
width = initial_code_size+1; |
166 |
max = (1<<width)-1; |
167 |
free = _EOF; |
168 |
memset(alphStack, 0xff, sizeof alphStack); |
169 |
memset(alphPrefix, 0xff, sizeof alphPrefix); |
170 |
memset(alphTail, 0xff, sizeof alphTail); |
171 |
if (!getlogbyte(stream, width, bitleft, byteleft, lbyte, curcode, bidx, buf)) return false; |
172 |
|
173 |
special = curcode; |
174 |
oldcode = curcode; |
175 |
goto g; |
176 |
} |
177 |
readb = curcode; |
178 |
if (curcode >= free) { |
179 |
// new code |
180 |
curcode = oldcode; |
181 |
alphStack[stackp++] = special; |
182 |
} |
183 |
// code in alphabet |
184 |
while (curcode > _CLR) { |
185 |
// decode |
186 |
alphStack[stackp++] = alphTail[curcode]; |
187 |
curcode = alphPrefix[curcode]; |
188 |
if (curcode == 0xffff) return false; |
189 |
} |
190 |
alphStack[stackp] = curcode; |
191 |
special = curcode; |
192 |
do { |
193 |
pic[idx++] = alphStack[stackp--]; |
194 |
} while (stackp >= 0); |
195 |
stackp = 0; |
196 |
alphPrefix[free] = oldcode; |
197 |
alphTail[free] = curcode; |
198 |
oldcode = readb; |
199 |
free++; |
200 |
if (free > max) { |
201 |
if (width < 12) { |
202 |
width++; |
203 |
max = (1<<width)-1; |
204 |
} |
205 |
} |
206 |
} |
207 |
stream.readx(buf, 2); |
208 |
if (buf[0] != 0x00 || buf[1] != 0x3b) return false; |
209 |
// ht_printf("mWidth: %d mHeight: %d\n", mWidth, mHeight); |
210 |
return true; |
211 |
} |
212 |
|
213 |
void Gif::draw(SystemDisplay *display, int x, int y) |
214 |
{ |
215 |
int p=0; |
216 |
switch (display->mClientChar.bytesPerPixel) { |
217 |
case 1: |
218 |
return; |
219 |
case 2: { |
220 |
byte *f = gFrameBuffer+y*display->mClientChar.width*2+x*2; |
221 |
for (int i=0; i<mHeight; i++) { |
222 |
for (int j=0; j<mWidth; j++) { |
223 |
int c = pic[p]*3; |
224 |
f[0]=((mPal[c]>>1)&0x7c)|(mPal[c+1]>>6); |
225 |
f[1]=((mPal[c+1]<<2)&0xe0)|(mPal[c+2]>>3); |
226 |
f+=2; |
227 |
p++; |
228 |
} |
229 |
f += display->mClientChar.scanLineLength - mWidth*2; |
230 |
} |
231 |
break; |
232 |
} |
233 |
case 4: { |
234 |
byte *f = gFrameBuffer+y*display->mClientChar.width*4+x*4; |
235 |
for (int i=0; i<mHeight; i++) { |
236 |
for (int j=0; j<mWidth; j++) { |
237 |
int c = pic[p]*3; |
238 |
f[0]=0; |
239 |
f[1]=mPal[c]; |
240 |
f[2]=mPal[c+1]; |
241 |
f[3]=mPal[c+2]; |
242 |
f+=4; |
243 |
p++; |
244 |
} |
245 |
f += display->mClientChar.scanLineLength - mWidth*4; |
246 |
} |
247 |
break; |
248 |
} |
249 |
default: |
250 |
ht_printf("unknown bytes per pixel in gif.cc\n"); |
251 |
exit(1); |
252 |
} |
253 |
damageFrameBuffer(y * display->mClientChar.width*2 + x*2); |
254 |
damageFrameBuffer((y+mHeight) * display->mClientChar.width*2 + (x+mWidth) * 2); |
255 |
} |