1 31 package Acme.JPM.Encoders; 32 33 import java.awt.*; 34 import java.awt.image.ImageProducer ; 35 import java.io.IOException ; 36 import java.io.OutputStream ; 37 import java.util.Enumeration ; 38 39 46 public class GifEncoder extends ImageEncoder { 47 48 private boolean interlace = false; 49 50 public GifEncoder(Image img, OutputStream out) throws IOException { 54 super(img, out); 55 } 56 57 public GifEncoder(Image img, OutputStream out, boolean interlace) throws IOException { 62 super(img, out); 63 this.interlace = interlace; 64 } 65 66 public GifEncoder(ImageProducer prod, OutputStream out) throws IOException { 70 super(prod, out); 71 } 72 73 public GifEncoder(ImageProducer prod, OutputStream out, boolean interlace) throws IOException { 77 super(prod, out); 78 this.interlace = interlace; 79 } 80 81 82 int width, height; 83 int[][] rgbPixels; 84 85 void encodeStart(int width, int height) throws IOException { 86 this.width = width; 87 this.height = height; 88 rgbPixels = new int[height][width]; 89 } 90 91 void encodePixels(int x, int y, int w, int h, int[] rgbPixels, int off, int scansize) 92 throws IOException { 93 for (int row = 0; row < h; ++row) 95 System.arraycopy(rgbPixels, row * scansize + off, 96 this.rgbPixels[y + row], x, w); 97 98 } 99 100 Acme.IntHashtable colorHash; 101 102 void encodeDone() throws IOException { 103 int transparentIndex = -1; 104 int transparentRgb = -1; 105 colorHash = new Acme.IntHashtable(); 107 int index = 0; 108 for (int row = 0; row < height; ++row) { 109 int rowOffset = row * width; 110 for (int col = 0; col < width; ++col) { 111 int rgb = rgbPixels[row][col]; 112 boolean isTransparent = ((rgb >>> 24) < 0x80); 113 if (isTransparent) { 114 if (transparentIndex < 0) { 115 transparentIndex = index; 117 transparentRgb = rgb; 118 } else if (rgb != transparentRgb) { 119 rgbPixels[row][col] = rgb = transparentRgb; 122 } 123 } 124 GifEncoderHashitem item = 125 (GifEncoderHashitem) colorHash.get(rgb); 126 if (item == null) { 127 if (index >= 256) 128 throw new IOException ("too many colors for a GIF"); 129 item = new GifEncoderHashitem(rgb, 1, index, isTransparent); 130 ++index; 131 colorHash.put(rgb, item); 132 } else 133 ++item.count; 134 } 135 } 136 137 int logColors; 139 if (index <= 2) 140 logColors = 1; 141 else if (index <= 4) 142 logColors = 2; 143 else if (index <= 16) 144 logColors = 4; 145 else 146 logColors = 8; 147 148 int mapSize = 1 << logColors; 150 byte[] reds = new byte[mapSize]; 151 byte[] grns = new byte[mapSize]; 152 byte[] blus = new byte[mapSize]; 153 for (Enumeration e = colorHash.elements(); e.hasMoreElements();) { 154 GifEncoderHashitem item = (GifEncoderHashitem) e.nextElement(); 155 reds[item.index] = (byte) ((item.rgb >> 16) & 0xff); 156 grns[item.index] = (byte) ((item.rgb >> 8) & 0xff); 157 blus[item.index] = (byte) (item.rgb & 0xff); 158 } 159 160 GIFEncode(out, width, height, interlace, (byte) 0, transparentIndex, 161 logColors, reds, grns, blus); 162 } 163 164 byte GetPixel(int x, int y) throws IOException { 165 GifEncoderHashitem item = 166 (GifEncoderHashitem) colorHash.get(rgbPixels[y][x]); 167 if (item == null) 168 throw new IOException ("color not found"); 169 return (byte) item.index; 170 } 171 172 static void writeString(OutputStream out, String str) throws IOException { 173 byte[] buf = str.getBytes(); 174 out.write(buf); 175 } 176 177 181 int Width, Height; 182 boolean Interlace; 183 int curx, cury; 184 int CountDown; 185 int Pass = 0; 186 187 void GIFEncode(OutputStream outs, int Width, int Height, boolean Interlace, byte Background, int Transparent, int BitsPerPixel, byte[] Red, byte[] Green, byte[] Blue) 188 throws IOException { 189 byte B; 190 int LeftOfs, TopOfs; 191 int ColorMapSize; 192 int InitCodeSize; 193 int i; 194 195 this.Width = Width; 196 this.Height = Height; 197 this.Interlace = Interlace; 198 ColorMapSize = 1 << BitsPerPixel; 199 LeftOfs = TopOfs = 0; 200 201 CountDown = Width * Height; 203 204 Pass = 0; 206 207 if (BitsPerPixel <= 1) 209 InitCodeSize = 2; 210 else 211 InitCodeSize = BitsPerPixel; 212 213 curx = 0; 215 cury = 0; 216 217 writeString(outs, "GIF89a"); 219 220 Putword(Width, outs); 222 Putword(Height, outs); 223 224 B = (byte) 0x80; B |= (byte) ((8 - 1) << 4); 228 B |= (byte) ((BitsPerPixel - 1)); 231 232 Putbyte(B, outs); 234 235 Putbyte(Background, outs); 237 238 Putbyte((byte) 0, outs); 245 246 for (i = 0; i < ColorMapSize; ++i) { 248 Putbyte(Red[i], outs); 249 Putbyte(Green[i], outs); 250 Putbyte(Blue[i], outs); 251 } 252 253 if (Transparent != -1) { 255 Putbyte((byte) '!', outs); 256 Putbyte((byte) 0xf9, outs); 257 Putbyte((byte) 4, outs); 258 Putbyte((byte) 1, outs); 259 Putbyte((byte) 0, outs); 260 Putbyte((byte) 0, outs); 261 Putbyte((byte) Transparent, outs); 262 Putbyte((byte) 0, outs); 263 } 264 265 Putbyte((byte) ',', outs); 267 268 Putword(LeftOfs, outs); 270 Putword(TopOfs, outs); 271 Putword(Width, outs); 272 Putword(Height, outs); 273 274 if (Interlace) 276 Putbyte((byte) 0x40, outs); 277 else 278 Putbyte((byte) 0x00, outs); 279 280 Putbyte((byte) InitCodeSize, outs); 282 283 compress(InitCodeSize + 1, outs); 285 286 Putbyte((byte) 0, outs); 288 289 Putbyte((byte) ';', outs); 291 } 292 293 void BumpPixel() { 295 ++curx; 297 298 if (curx == Width) { 302 curx = 0; 303 304 if (!Interlace) 305 ++cury; 306 else { 307 switch (Pass) { 308 case 0: 309 cury += 8; 310 if (cury >= Height) { 311 ++Pass; 312 cury = 4; 313 } 314 break; 315 316 case 1: 317 cury += 8; 318 if (cury >= Height) { 319 ++Pass; 320 cury = 2; 321 } 322 break; 323 324 case 2: 325 cury += 4; 326 if (cury >= Height) { 327 ++Pass; 328 cury = 1; 329 } 330 break; 331 332 case 3: 333 cury += 2; 334 break; 335 } 336 } 337 } 338 } 339 340 static final int EOF = -1; 341 342 int GIFNextPixel() throws IOException { 344 byte r; 345 346 if (CountDown == 0) 347 return EOF; 348 349 --CountDown; 350 351 r = GetPixel(curx, cury); 352 353 BumpPixel(); 354 355 return r & 0xff; 356 } 357 358 void Putword(int w, OutputStream outs) throws IOException { 360 Putbyte((byte) (w & 0xff), outs); 361 Putbyte((byte) ((w >> 8) & 0xff), outs); 362 } 363 364 void Putbyte(byte b, OutputStream outs) throws IOException { 366 outs.write(b); 367 } 368 369 370 375 377 static final int BITS = 12; 378 379 static final int HSIZE = 5003; 381 392 int n_bits; int maxbits = BITS; int maxcode; int maxmaxcode = 1 << BITS; 397 final int MAXCODE(int n_bits) { 398 return (1 << n_bits) - 1; 399 } 400 401 int[] htab = new int[HSIZE]; 402 int[] codetab = new int[HSIZE]; 403 404 int hsize = HSIZE; 406 int free_ent = 0; 408 boolean clear_flg = false; 411 412 424 int g_init_bits; 425 426 int ClearCode; 427 int EOFCode; 428 429 void compress(int init_bits, OutputStream outs) throws IOException { 430 int fcode; 431 int i ; 432 int c; 433 int ent; 434 int disp; 435 int hsize_reg; 436 int hshift; 437 438 g_init_bits = init_bits; 440 441 clear_flg = false; 443 n_bits = g_init_bits; 444 maxcode = MAXCODE(n_bits); 445 446 ClearCode = 1 << (init_bits - 1); 447 EOFCode = ClearCode + 1; 448 free_ent = ClearCode + 2; 449 450 char_init(); 451 452 ent = GIFNextPixel(); 453 454 hshift = 0; 455 for (fcode = hsize; fcode < 65536; fcode *= 2) 456 ++hshift; 457 hshift = 8 - hshift; 459 hsize_reg = hsize; 460 cl_hash(hsize_reg); 462 output(ClearCode, outs); 463 464 outer_loop: 465 while ((c = GIFNextPixel()) != EOF) { 466 fcode = (c << maxbits) + ent; 467 i = (c << hshift) ^ ent; 469 if (htab[i] == fcode) { 470 ent = codetab[i]; 471 continue; 472 } else if (htab[i] >= 0) { 474 disp = hsize_reg - i; if (i == 0) 476 disp = 1; 477 do { 478 if ((i -= disp) < 0) 479 i += hsize_reg; 480 481 if (htab[i] == fcode) { 482 ent = codetab[i]; 483 continue outer_loop; 484 } 485 } while (htab[i] >= 0); 486 } 487 output(ent, outs); 488 ent = c; 489 if (free_ent < maxmaxcode) { 490 codetab[i] = free_ent++; htab[i] = fcode; 492 } else 493 cl_block(outs); 494 } 495 output(ent, outs); 497 output(EOFCode, outs); 498 } 499 500 515 int cur_accum = 0; 516 int cur_bits = 0; 517 518 int masks[] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 519 0x001F, 0x003F, 0x007F, 0x00FF, 520 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 521 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}; 522 523 void output(int code, OutputStream outs) throws IOException { 524 cur_accum &= masks[cur_bits]; 525 526 if (cur_bits > 0) 527 cur_accum |= (code << cur_bits); 528 else 529 cur_accum = code; 530 531 cur_bits += n_bits; 532 533 while (cur_bits >= 8) { 534 char_out((byte) (cur_accum & 0xff), outs); 535 cur_accum >>= 8; 536 cur_bits -= 8; 537 } 538 539 if (free_ent > maxcode || clear_flg) { 542 if (clear_flg) { 543 maxcode = MAXCODE(n_bits = g_init_bits); 544 clear_flg = false; 545 } else { 546 ++n_bits; 547 if (n_bits == maxbits) 548 maxcode = maxmaxcode; 549 else 550 maxcode = MAXCODE(n_bits); 551 } 552 } 553 554 if (code == EOFCode) { 555 while (cur_bits > 0) { 557 char_out((byte) (cur_accum & 0xff), outs); 558 cur_accum >>= 8; 559 cur_bits -= 8; 560 } 561 562 flush_char(outs); 563 } 564 } 565 566 568 void cl_block(OutputStream outs) throws IOException { 570 cl_hash(hsize); 571 free_ent = ClearCode + 2; 572 clear_flg = true; 573 574 output(ClearCode, outs); 575 } 576 577 void cl_hash(int hsize) { 579 for (int i = 0; i < hsize; ++i) 580 htab[i] = -1; 581 } 582 583 585 int a_count; 587 588 void char_init() { 590 a_count = 0; 591 } 592 593 byte[] accum = new byte[256]; 595 596 void char_out(byte c, OutputStream outs) throws IOException { 599 accum[a_count++] = c; 600 if (a_count >= 254) 601 flush_char(outs); 602 } 603 604 void flush_char(OutputStream outs) throws IOException { 606 if (a_count > 0) { 607 outs.write(a_count); 608 outs.write(accum, 0, a_count); 609 a_count = 0; 610 } 611 } 612 613 } 614 615 class GifEncoderHashitem { 616 617 public int rgb; 618 public int count; 619 public int index; 620 public boolean isTransparent; 621 622 public GifEncoderHashitem(int rgb, int count, int index, boolean isTransparent) { 623 this.rgb = rgb; 624 this.count = count; 625 this.index = index; 626 this.isTransparent = isTransparent; 627 } 628 629 } 630 | Popular Tags |