1implement RImagefile; 2 3include "sys.m"; 4 sys: Sys; 5 6include "draw.m"; 7 8include "bufio.m"; 9 bufio: Bufio; 10 Iobuf: import bufio; 11 12include "imagefile.m"; 13 14Header: adt 15{ 16 fd: ref Iobuf; 17 buf: array of byte; 18 vers: string; 19 screenw: int; 20 screenh: int; 21 fields: int; 22 bgrnd: int; 23 aspect: int; 24 transp: int; 25 trindex: byte; 26}; 27 28Entry: adt 29{ 30 prefix: int; 31 exten: int; 32}; 33 34tbl: array of Entry; 35 36init(iomod: Bufio) 37{ 38 if(sys == nil) 39 sys = load Sys Sys->PATH; 40 bufio = iomod; 41} 42read(fd: ref Iobuf): (ref Rawimage, string) 43{ 44 (a, err) := readarray(fd, 0); 45 if(a != nil) 46 return (a[0], err); 47 return (nil, err); 48} 49 50readmulti(fd: ref Iobuf): (array of ref Rawimage, string) 51{ 52 return readarray(fd, 1); 53} 54 55readarray(fd: ref Iobuf, multi: int): (array of ref Rawimage, string) 56{ 57 inittbl(); 58 59 buf := array[3*256] of byte; 60 61 (header, err) := readheader(fd, buf); 62 if(header == nil) 63 return (nil, err); 64 65 globalcmap: array of byte; 66 if(header.fields & 16r80){ 67 (globalcmap, err) = readcmap(header, (header.fields&7)+1); 68 if(globalcmap == nil) 69 return (nil, err); 70 } 71 72 images: array of ref Rawimage; 73 new: ref Rawimage; 74 75 Loop: 76 for(;;){ 77 case c := fd.getb(){ 78 Bufio->EOF => 79 if(err == "") 80 err = "ReadGIF: premature EOF"; 81 break Loop; 82 Bufio->ERROR => 83 err = sys->sprint("ReadGIF: read error: %r"); 84 return (nil, err); 85 16r21 => # Extension (ignored) 86 err = skipextension(header); 87 if(err != nil) 88 return (nil, err); 89 90 16r2C => # Image Descriptor 91 if(!multi && images!=nil) # why read the rest? 92 break Loop; 93 (new, err) = readimage(header); 94 if(new == nil) 95 return (nil ,err); 96 if(new.fields & 16r80){ 97 (new.cmap, err) = readcmap(header, (new.fields&7)+1); 98 if(new.cmap == nil) 99 return (nil, err); 100 }else 101 new.cmap = globalcmap; 102 (new.chans[0], err) = decode(header, new); 103 if(new.chans[0] == nil) 104 return (nil, err); 105 if(new.fields & 16r40) 106 interlace(new); 107 new.transp = header.transp; 108 new.trindex = header.trindex; 109 nimages := array[len images+1] of ref Rawimage; 110 nimages[0:] = images[0:]; 111 nimages[len images] = new; 112 images = nimages; 113 114 16r3B => # Trailer 115 break Loop; 116 117 * => 118 err = sys->sprint("ReadGIF: unknown block type: %x", c); 119 break Loop; 120 } 121 } 122 123 if(images==nil || images[0].chans[0] == nil){ 124 if(err == nil) 125 err = "ReadGIF: no picture in file"; 126 return (nil, err); 127 } 128 129 return (images, err); 130} 131 132readheader(fd: ref Iobuf, buf: array of byte): (ref Header, string) 133{ 134 if(fd.read(buf, 13) != 13){ 135 err := sys->sprint("ReadGIF: can't read header: %r"); 136 return (nil, err); 137 } 138 h := ref Header; 139 h.vers = string buf[0:6]; 140 if(h.vers!="GIF87a" && h.vers!="GIF89a"){ 141 err := sys->sprint("ReadGIF: can't recognize format %s", h.vers); 142 return (nil, err); 143 } 144 h.screenw = int buf[6]+(int buf[7]<<8); 145 h.screenh = int buf[8]+(int buf[9]<<8); 146 h.fields = int buf[10]; 147 h.bgrnd = int buf[11]; 148 h.aspect = int buf[12]; 149 h.fd = fd; 150 h.buf = buf; 151 h.transp = 0; 152 return (h, ""); 153} 154 155readcmap(h: ref Header, size: int): (array of byte,string) 156{ 157 size = 3*(1<<size); 158 map := array[size] of byte; 159 if(h.fd.read(map, size) != size) 160 return (nil, "ReadGIF: short read on color map"); 161 return (map, ""); 162} 163 164readimage(h: ref Header): (ref Rawimage, string) 165{ 166 if(h.fd.read(h.buf, 9) != 9){ 167 err := sys->sprint("ReadGIF: can't read image descriptor: %r"); 168 return (nil, err); 169 } 170 i := ref Rawimage; 171 left := int h.buf[0]+(int h.buf[1]<<8); 172 top := int h.buf[2]+(int h.buf[3]<<8); 173 width := int h.buf[4]+(int h.buf[5]<<8); 174 height := int h.buf[6]+(int h.buf[7]<<8); 175 i.fields = int h.buf[8]; 176 i.r.min.x = left; 177 i.r.min.y = top; 178 i.r.max.x = left+width; 179 i.r.max.y = top+height; 180 i.nchans = 1; 181 i.chans = array[1] of array of byte; 182 i.chandesc = CRGB1; 183 return (i, ""); 184} 185 186readdata(h: ref Header, ch: chan of (array of byte, string)) 187{ 188 err: string; 189 190 # send nil for error, buffer of length 0 for EOF 191 for(;;){ 192 nbytes := h.fd.getb(); 193 if(nbytes < 0){ 194 err = sys->sprint("ReadGIF: can't read data: %r"); 195 ch <-= (nil, err); 196 return; 197 } 198 d := array[nbytes] of byte; 199 if(nbytes == 0){ 200 ch <-= (d, ""); 201 return; 202 } 203 n := h.fd.read(d, nbytes); 204 if(n != nbytes){ 205 if(n > 0){ 206 ch <-= (d[0:n], nil); 207 ch <-= (d[0:0], "ReadGIF: short data subblock"); 208 }else 209 ch <-= (nil, sys->sprint("ReadGIF: can't read data: %r")); 210 return; 211 } 212 ch <-= (d, ""); 213 } 214} 215 216readerr: con "ReadGIF: can't read extension: %r"; 217 218skipextension(h: ref Header): string 219{ 220 fmterr: con "ReadGIF: bad extension format"; 221 222 hsize := 0; 223 hasdata := 0; 224 225 case h.fd.getb(){ 226 Bufio->ERROR or Bufio->EOF => 227 return sys->sprint(readerr); 228 16r01 => # Plain Text Extension 229 hsize = 13; 230 hasdata = 1; 231 16rF9 => # Graphic Control Extension 232 return graphiccontrol(h); 233 16rFE => # Comment Extension 234 hasdata = 1; 235 16rFF => # Application Extension 236 hsize = h.fd.getb(); 237 # standard says this must be 11, but Adobe likes to put out 10-byte ones, 238 # so we pay attention to the field. 239 hasdata = 1; 240 * => 241 return "ReadGIF: unknown extension"; 242 } 243 if(hsize>0 && h.fd.read(h.buf, hsize) != hsize) 244 return sys->sprint(readerr); 245 if(!hasdata){ 246 if(int h.buf[hsize-1] != 0) 247 return fmterr; 248 }else{ 249 ch := chan of (array of byte, string); 250 spawn readdata(h, ch); 251 for(;;){ 252 (data, err) := <-ch; 253 if(data == nil) 254 return err; 255 if(len data == 0) 256 break; 257 } 258 } 259 return ""; 260} 261 262graphiccontrol(h: ref Header): string 263{ 264 if(h.fd.read(h.buf, 5+1) != 5+1) 265 return sys->sprint(readerr); 266 if(int h.buf[1] & 1){ 267 h.transp = 1; 268 h.trindex = h.buf[4]; 269 } 270 return ""; 271} 272 273inittbl() 274{ 275 tbl = array[4096] of Entry; 276 for(i:=0; i<258; i++) { 277 tbl[i].prefix = -1; 278 tbl[i].exten = i; 279 } 280} 281 282decode(h: ref Header, i: ref Rawimage): (array of byte, string) 283{ 284 c, incode: int; 285 286 err := ""; 287 if(h.fd.read(h.buf, 1) != 1){ 288 err = sys->sprint("ReadGIF: can't read data: %r"); 289 return (nil, err); 290 } 291 codesize := int h.buf[0]; 292 if(codesize>8 || 0>codesize){ 293 err = sys->sprint("ReadGIF: can't handle codesize %d", codesize); 294 return (nil, err); 295 } 296 err1 := ""; 297 if(i.cmap!=nil && len i.cmap!=3*(1<<codesize) 298 && (codesize!=2 || len i.cmap!=3*2)) # peculiar GIF bitmap files... 299 err1 = sys->sprint("ReadGIF: codesize %d doesn't match color map 3*%d", codesize, len i.cmap/3); 300 301 ch := chan of (array of byte, string); 302 303 spawn readdata(h, ch); 304 305 CTM :=1<<codesize; 306 EOD := CTM+1; 307 308 pic := array[(i.r.max.x-i.r.min.x)*(i.r.max.y-i.r.min.y)] of byte; 309 pici := 0; 310 data := array[0] of byte; 311 datai := 0; 312 313 nbits := 0; 314 sreg := 0; 315 stack := array[4096] of byte; 316 stacki: int; 317 fc := 0; 318 319Init: 320 for(;;){ 321 csize := codesize+1; 322 nentry := EOD+1; 323 maxentry := (1<<csize)-1; 324 first := 1; 325 ocode := -1; 326 327 for(;; ocode = incode) { 328 while(nbits < csize) { 329 if(datai == len data){ 330 (data, err) = <-ch; 331 if(data == nil) 332 return (nil, err); 333 if(err!="" && err1=="") 334 err1 = err; 335 if(len data == 0) 336 break Init; 337 datai = 0; 338 } 339 c = int data[datai++]; 340 sreg |= c<<nbits; 341 nbits += 8; 342 } 343 code := sreg & ((1<<csize) - 1); 344 sreg >>= csize; 345 nbits -= csize; 346 347 if(code == EOD){ 348 (data, err) = <-ch; 349 if(len data != 0) 350 err = "ReadGIF: unexpected data past EOD"; 351 if(err!="" && err1=="") 352 err1 = err; 353 break Init; 354 } 355 356 if(code == CTM) 357 continue Init; 358 359 stacki = len stack-1; 360 361 incode = code; 362 363 # special case for KwKwK 364 if(code == nentry) { 365 stack[stacki--] = byte fc; 366 code = ocode; 367 } 368 369 if(code > nentry) { 370 err = sys->sprint("ReadGIF: bad code %x %x", code, nentry); 371 return (nil, err); 372 } 373 374 for(c=code; c>=0; c=tbl[c].prefix) 375 stack[stacki--] = byte tbl[c].exten; 376 377 nb := len stack-(stacki+1); 378 if(pici+nb > len pic){ 379 if(err1 == "") 380 err1 = "ReadGIF: data overflows picture"; 381 }else{ 382 pic[pici:] = stack[stacki+1:]; 383 pici += nb; 384 } 385 386 fc = int stack[stacki+1]; 387 388 if(first){ 389 first = 0; 390 continue; 391 } 392 early:=0; # peculiar tiff feature here for reference 393 if(nentry == maxentry-early) { 394 if(csize >= 12) 395 continue; 396 csize++; 397 maxentry = (1<<csize); 398 if(csize < 12) 399 maxentry--; 400 } 401 tbl[nentry].prefix = ocode; 402 tbl[nentry].exten = fc; 403 nentry++; 404 } 405 } 406 return (pic, err1); 407} 408 409interlace(image: ref Rawimage) 410{ 411 pic := image.chans[0]; 412 r := image.r; 413 dx := r.max.x-r.min.x; 414 ipic := array[dx*(r.max.y-r.min.y)] of byte; 415 416 # Group 1: every 8th row, starting with row 0 417 yy := 0; 418 for(y:=r.min.y; y<r.max.y; y+=8){ 419 ipic[y*dx:] = pic[yy*dx:(yy+1)*dx]; 420 yy++; 421 } 422 423 # Group 2: every 8th row, starting with row 4 424 for(y=r.min.y+4; y<r.max.y; y+=8){ 425 ipic[y*dx:] = pic[yy*dx:(yy+1)*dx]; 426 yy++; 427 } 428 429 # Group 3: every 4th row, starting with row 2 430 for(y=r.min.y+2; y<r.max.y; y+=4){ 431 ipic[y*dx:] = pic[yy*dx:(yy+1)*dx]; 432 yy++; 433 } 434 435 # Group 4: every 2nd row, starting with row 1 436 for(y=r.min.y+1; y<r.max.y; y+=2){ 437 ipic[y*dx:] = pic[yy*dx:(yy+1)*dx]; 438 yy++; 439 } 440 441 image.chans[0] = ipic; 442} 443