├── Components ├── AnimatedGifEncoder.cs ├── AssemblyInfo.cs ├── Components.csproj ├── Gif.resharperoptions ├── GifDecoder.cs ├── LZWEncoder.cs ├── NeuQuant.cs └── _ReSharper.Gif │ ├── IModuleManager │ └── __cachecheck │ ├── IPersistenceManager │ ├── 0 │ └── __cachecheck │ ├── SymbolCache │ ├── 14 │ ├── 15 │ ├── 16 │ ├── 17 │ ├── 18 │ ├── 19 │ ├── 20 │ ├── 21 │ ├── 22 │ ├── 23 │ ├── 24 │ ├── 25 │ ├── 26 │ └── __cachecheck │ ├── WordIndex │ ├── 19 │ ├── 21 │ ├── 22 │ ├── 23 │ ├── 24 │ └── __cachecheck │ └── __cachecheck ├── Example ├── App.ico ├── AssemblyInfo.cs ├── Example.csproj ├── ExampleMain.cs └── Res │ ├── 01.png │ ├── 02.png │ ├── 03.png │ └── test.gif ├── NGif.sln ├── README.md └── Refs └── gif.zip /Components/AnimatedGifEncoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.IO; 4 | 5 | #region .NET Disclaimer/Info 6 | //=============================================================================== 7 | // 8 | // gOODiDEA, uland.com 9 | //=============================================================================== 10 | // 11 | // $Header : $ 12 | // $Author : $ 13 | // $Date : $ 14 | // $Revision: $ 15 | // $History: $ 16 | // 17 | //=============================================================================== 18 | #endregion 19 | 20 | #region Java 21 | /** 22 | * Class AnimatedGifEncoder - Encodes a GIF file consisting of one or 23 | * more frames. 24 | *
 25 |  * Example:
 26 |  *    AnimatedGifEncoder e = new AnimatedGifEncoder();
 27 |  *    e.start(outputFileName);
 28 |  *    e.setDelay(1000);   // 1 frame per sec
 29 |  *    e.addFrame(image1);
 30 |  *    e.addFrame(image2);
 31 |  *    e.finish();
 32 |  * 
33 | * No copyright asserted on the source code of this class. May be used 34 | * for any purpose, however, refer to the Unisys LZW patent for restrictions 35 | * on use of the associated LZWEncoder class. Please forward any corrections 36 | * to kweiner@fmsware.com. 37 | * 38 | * @author Kevin Weiner, FM Software 39 | * @version 1.03 November 2003 40 | * 41 | */ 42 | #endregion 43 | 44 | #region Modified By Phil Garcia 45 | /* 46 | * Modified by Phil Garcia (phil@thinkedge.com) 47 | 1. Add support to output the Gif to a MemoryStream (9/2/2005) 48 | 49 | */ 50 | #endregion 51 | 52 | namespace Gif.Components 53 | { 54 | public class AnimatedGifEncoder 55 | { 56 | protected int width; // image size 57 | protected int height; 58 | protected Color transparent = Color.Empty; // transparent color if given 59 | protected int transIndex; // transparent index in color table 60 | protected int repeat = -1; // no repeat 61 | protected int delay = 0; // frame delay (hundredths) 62 | protected bool started = false; // ready to output frames 63 | // protected BinaryWriter bw; 64 | protected MemoryStream ms; 65 | // protected FileStream fs; 66 | 67 | protected Image image; // current frame 68 | protected byte[] pixels; // BGR byte array from frame 69 | protected byte[] indexedPixels; // converted frame indexed to palette 70 | protected int colorDepth; // number of bit planes 71 | protected byte[] colorTab; // RGB palette 72 | protected bool[] usedEntry = new bool[256]; // active palette entries 73 | protected int palSize = 7; // color table size (bits-1) 74 | protected int dispose = -1; // disposal code (-1 = use default) 75 | protected bool closeStream = false; // close stream when finished 76 | protected bool firstFrame = true; 77 | protected bool sizeSet = false; // if false, get size from first frame 78 | protected int sample = 10; // default sample interval for quantizer 79 | 80 | /** 81 | * Sets the delay time between each frame, or changes it 82 | * for subsequent frames (applies to last frame added). 83 | * 84 | * @param ms int delay time in milliseconds 85 | */ 86 | public void SetDelay(int ms) 87 | { 88 | delay = (int)Math.Round(ms / 10.0f); 89 | } 90 | 91 | /** 92 | * Sets the GIF frame disposal code for the last added frame 93 | * and any subsequent frames. Default is 0 if no transparent 94 | * color has been set, otherwise 2. 95 | * @param code int disposal code. 96 | */ 97 | public void SetDispose(int code) 98 | { 99 | if (code >= 0) 100 | { 101 | dispose = code; 102 | } 103 | } 104 | 105 | /** 106 | * Sets the number of times the set of GIF frames 107 | * should be played. Default is 1; 0 means play 108 | * indefinitely. Must be invoked before the first 109 | * image is added. 110 | * 111 | * @param iter int number of iterations. 112 | * @return 113 | */ 114 | public void SetRepeat(int iter) 115 | { 116 | if (iter >= 0) 117 | { 118 | repeat = iter; 119 | } 120 | } 121 | 122 | /** 123 | * Sets the transparent color for the last added frame 124 | * and any subsequent frames. 125 | * Since all colors are subject to modification 126 | * in the quantization process, the color in the final 127 | * palette for each frame closest to the given color 128 | * becomes the transparent color for that frame. 129 | * May be set to null to indicate no transparent color. 130 | * 131 | * @param c Color to be treated as transparent on display. 132 | */ 133 | public void SetTransparent(Color c) 134 | { 135 | transparent = c; 136 | } 137 | 138 | /** 139 | * Adds next GIF frame. The frame is not written immediately, but is 140 | * actually deferred until the next frame is received so that timing 141 | * data can be inserted. Invoking finish() flushes all 142 | * frames. If setSize was not invoked, the size of the 143 | * first image is used for all subsequent frames. 144 | * 145 | * @param im BufferedImage containing frame to write. 146 | * @return true if successful. 147 | */ 148 | public bool AddFrame(Image im) 149 | { 150 | if ((im == null) || !started) 151 | { 152 | return false; 153 | } 154 | bool ok = true; 155 | try 156 | { 157 | if (!sizeSet) 158 | { 159 | // use first frame's size 160 | SetSize(im.Width, im.Height); 161 | } 162 | image = im; 163 | GetImagePixels(); // convert to correct format if necessary 164 | AnalyzePixels(); // build color table & map pixels 165 | if (firstFrame) 166 | { 167 | WriteLSD(); // logical screen descriptior 168 | WritePalette(); // global color table 169 | if (repeat >= 0) 170 | { 171 | // use NS app extension to indicate reps 172 | WriteNetscapeExt(); 173 | } 174 | } 175 | WriteGraphicCtrlExt(); // write graphic control extension 176 | WriteImageDesc(); // image descriptor 177 | if (!firstFrame) 178 | { 179 | WritePalette(); // local color table 180 | } 181 | WritePixels(); // encode and write pixel data 182 | firstFrame = false; 183 | } 184 | catch (IOException) 185 | { 186 | ok = false; 187 | } 188 | 189 | return ok; 190 | } 191 | 192 | /** 193 | * Flushes any pending data and closes output file. 194 | * If writing to an OutputStream, the stream is not 195 | * closed. 196 | */ 197 | public bool Finish() 198 | { 199 | if (!started) return false; 200 | bool ok = true; 201 | started = false; 202 | try 203 | { 204 | ms.WriteByte(0x3b); // gif trailer 205 | ms.Flush(); 206 | if (closeStream) 207 | { 208 | // ms.Close(); 209 | } 210 | } 211 | catch (IOException) 212 | { 213 | ok = false; 214 | } 215 | 216 | // reset for subsequent use 217 | transIndex = 0; 218 | // fs = null; 219 | image = null; 220 | pixels = null; 221 | indexedPixels = null; 222 | colorTab = null; 223 | closeStream = false; 224 | firstFrame = true; 225 | 226 | return ok; 227 | } 228 | 229 | /** 230 | * Sets frame rate in frames per second. Equivalent to 231 | * setDelay(1000/fps). 232 | * 233 | * @param fps float frame rate (frames per second) 234 | */ 235 | public void SetFrameRate(float fps) 236 | { 237 | if (fps != 0f) 238 | { 239 | delay = (int)Math.Round(100f / fps); 240 | } 241 | } 242 | 243 | /** 244 | * Sets quality of color quantization (conversion of images 245 | * to the maximum 256 colors allowed by the GIF specification). 246 | * Lower values (minimum = 1) produce better colors, but slow 247 | * processing significantly. 10 is the default, and produces 248 | * good color mapping at reasonable speeds. Values greater 249 | * than 20 do not yield significant improvements in speed. 250 | * 251 | * @param quality int greater than 0. 252 | * @return 253 | */ 254 | public void SetQuality(int quality) 255 | { 256 | if (quality < 1) quality = 1; 257 | sample = quality; 258 | } 259 | 260 | /** 261 | * Sets the GIF frame size. The default size is the 262 | * size of the first frame added if this method is 263 | * not invoked. 264 | * 265 | * @param w int frame width. 266 | * @param h int frame width. 267 | */ 268 | public void SetSize(int w, int h) 269 | { 270 | if (started && !firstFrame) return; 271 | width = w; 272 | height = h; 273 | if (width < 1) width = 320; 274 | if (height < 1) height = 240; 275 | sizeSet = true; 276 | } 277 | 278 | /** 279 | * Initiates GIF file creation on the given stream. The stream 280 | * is not closed automatically. 281 | * 282 | * @param os OutputStream on which GIF images are written. 283 | * @return false if initial write failed. 284 | */ 285 | 286 | public bool Start(MemoryStream os) 287 | { 288 | if (os == null) return false; 289 | bool ok = true; 290 | closeStream = false; 291 | ms = os; 292 | try 293 | { 294 | WriteString("GIF89a"); // header 295 | } 296 | catch (IOException) 297 | { 298 | ok = false; 299 | } 300 | return started = ok; 301 | } 302 | 303 | /** 304 | * Initiates writing of a GIF file to a memory stream. 305 | * 306 | * @return false if open or initial write failed. 307 | */ 308 | public bool Start() 309 | { 310 | bool ok = true; 311 | try 312 | { 313 | ok = Start(new MemoryStream(10 * 1024)); 314 | closeStream = true; 315 | } 316 | catch (IOException) 317 | { 318 | ok = false; 319 | } 320 | return started = ok; 321 | } 322 | 323 | /** 324 | * Initiates writing of a GIF file with the specified name. 325 | * 326 | * @return false if open or initial write failed. 327 | */ 328 | public bool Output(string file) 329 | { 330 | try 331 | { 332 | FileStream fs = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); 333 | fs.Write(ms.ToArray(), 0, (int)ms.Length); 334 | fs.Close(); 335 | } 336 | catch (IOException) 337 | { 338 | return false; 339 | } 340 | return true; 341 | } 342 | 343 | public MemoryStream Output() 344 | { 345 | return ms; 346 | } 347 | 348 | /** 349 | * Analyzes image colors and creates color map. 350 | */ 351 | protected void AnalyzePixels() 352 | { 353 | int len = pixels.Length; 354 | int nPix = len / 3; 355 | indexedPixels = new byte[nPix]; 356 | NeuQuant nq = new NeuQuant(pixels, len, sample); 357 | // initialize quantizer 358 | colorTab = nq.Process(); // create reduced palette 359 | // convert map from BGR to RGB 360 | // for (int i = 0; i < colorTab.Length; i += 3) 361 | // { 362 | // byte temp = colorTab[i]; 363 | // colorTab[i] = colorTab[i + 2]; 364 | // colorTab[i + 2] = temp; 365 | // usedEntry[i / 3] = false; 366 | // } 367 | // map image pixels to new palette 368 | int k = 0; 369 | for (int i = 0; i < nPix; i++) 370 | { 371 | int index = 372 | nq.Map(pixels[k++] & 0xff, 373 | pixels[k++] & 0xff, 374 | pixels[k++] & 0xff); 375 | usedEntry[index] = true; 376 | indexedPixels[i] = (byte)index; 377 | } 378 | pixels = null; 379 | colorDepth = 8; 380 | palSize = 7; 381 | // get closest match to transparent color if specified 382 | if (transparent != Color.Empty) 383 | { 384 | //transIndex = FindClosest(transparent); 385 | transIndex = nq.Map(transparent.B, transparent.G, transparent.R); 386 | } 387 | } 388 | 389 | /** 390 | * Returns index of palette color closest to c 391 | * 392 | */ 393 | protected int FindClosest(Color c) 394 | { 395 | if (colorTab == null) return -1; 396 | int r = c.R; 397 | int g = c.G; 398 | int b = c.B; 399 | int minpos = 0; 400 | int dmin = 256 * 256 * 256; 401 | int len = colorTab.Length; 402 | for (int i = 0; i < len;) 403 | { 404 | int dr = r - (colorTab[i++] & 0xff); 405 | int dg = g - (colorTab[i++] & 0xff); 406 | int db = b - (colorTab[i] & 0xff); 407 | int d = dr * dr + dg * dg + db * db; 408 | int index = i / 3; 409 | if (usedEntry[index] && (d < dmin)) 410 | { 411 | dmin = d; 412 | minpos = index; 413 | } 414 | i++; 415 | } 416 | return minpos; 417 | } 418 | 419 | /** 420 | * Extracts image pixels into byte array "pixels" 421 | */ 422 | protected void GetImagePixels() 423 | { 424 | int w = image.Width; 425 | int h = image.Height; 426 | // int type = image.GetType().; 427 | if ((w != width) 428 | || (h != height) 429 | ) 430 | { 431 | // create new image with right size/format 432 | Image temp = 433 | new Bitmap(width, height); 434 | Graphics g = Graphics.FromImage(temp); 435 | g.DrawImage(image, 0, 0); 436 | image = temp; 437 | g.Dispose(); 438 | } 439 | /* 440 | ToDo: 441 | improve performance: use unsafe code 442 | */ 443 | pixels = new Byte[3 * image.Width * image.Height]; 444 | int count = 0; 445 | Bitmap tempBitmap = new Bitmap(image); 446 | for (int th = 0; th < image.Height; th++) 447 | { 448 | for (int tw = 0; tw < image.Width; tw++) 449 | { 450 | Color color = tempBitmap.GetPixel(tw, th); 451 | pixels[count] = color.R; 452 | count++; 453 | pixels[count] = color.G; 454 | count++; 455 | pixels[count] = color.B; 456 | count++; 457 | } 458 | } 459 | 460 | // pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); 461 | } 462 | 463 | /** 464 | * Writes Graphic Control Extension 465 | */ 466 | protected void WriteGraphicCtrlExt() 467 | { 468 | ms.WriteByte(0x21); // extension introducer 469 | ms.WriteByte(0xf9); // GCE label 470 | ms.WriteByte(4); // data block size 471 | int transp, disp; 472 | if (transparent == Color.Empty) 473 | { 474 | transp = 0; 475 | disp = 0; // dispose = no action 476 | } 477 | else 478 | { 479 | transp = 1; 480 | disp = 2; // force clear if using transparent color 481 | } 482 | if (dispose >= 0) 483 | { 484 | disp = dispose & 7; // user override 485 | } 486 | disp <<= 2; 487 | 488 | // packed fields 489 | ms.WriteByte(Convert.ToByte(0 | // 1:3 reserved 490 | disp | // 4:6 disposal 491 | 0 | // 7 user input - 0 = none 492 | transp)); // 8 transparency flag 493 | 494 | WriteShort(delay); // delay x 1/100 sec 495 | ms.WriteByte(Convert.ToByte(transIndex)); // transparent color index 496 | ms.WriteByte(0); // block terminator 497 | } 498 | 499 | /** 500 | * Writes Image Descriptor 501 | */ 502 | protected void WriteImageDesc() 503 | { 504 | ms.WriteByte(0x2c); // image separator 505 | WriteShort(0); // image position x,y = 0,0 506 | WriteShort(0); 507 | WriteShort(width); // image size 508 | WriteShort(height); 509 | // packed fields 510 | if (firstFrame) 511 | { 512 | // no LCT - GCT is used for first (or only) frame 513 | ms.WriteByte(0); 514 | } 515 | else 516 | { 517 | // specify normal LCT 518 | ms.WriteByte(Convert.ToByte(0x80 | // 1 local color table 1=yes 519 | 0 | // 2 interlace - 0=no 520 | 0 | // 3 sorted - 0=no 521 | 0 | // 4-5 reserved 522 | palSize)); // 6-8 size of color table 523 | } 524 | } 525 | 526 | /** 527 | * Writes Logical Screen Descriptor 528 | */ 529 | protected void WriteLSD() 530 | { 531 | // logical screen size 532 | WriteShort(width); 533 | WriteShort(height); 534 | // packed fields 535 | ms.WriteByte(Convert.ToByte(0x80 | // 1 : global color table flag = 1 (gct used) 536 | 0x70 | // 2-4 : color resolution = 7 537 | 0x00 | // 5 : gct sort flag = 0 538 | palSize)); // 6-8 : gct size 539 | 540 | ms.WriteByte(0); // background color index 541 | ms.WriteByte(0); // pixel aspect ratio - assume 1:1 542 | } 543 | 544 | /** 545 | * Writes Netscape application extension to define 546 | * repeat count. 547 | */ 548 | protected void WriteNetscapeExt() 549 | { 550 | ms.WriteByte(0x21); // extension introducer 551 | ms.WriteByte(0xff); // app extension label 552 | ms.WriteByte(11); // block size 553 | WriteString("NETSCAPE" + "2.0"); // app id + auth code 554 | ms.WriteByte(3); // sub-block size 555 | ms.WriteByte(1); // loop sub-block id 556 | WriteShort(repeat); // loop count (extra iterations, 0=repeat forever) 557 | ms.WriteByte(0); // block terminator 558 | } 559 | 560 | /** 561 | * Writes color table 562 | */ 563 | protected void WritePalette() 564 | { 565 | ms.Write(colorTab, 0, colorTab.Length); 566 | int n = (3 * 256) - colorTab.Length; 567 | for (int i = 0; i < n; i++) 568 | { 569 | ms.WriteByte(0); 570 | } 571 | } 572 | 573 | /** 574 | * Encodes and writes pixel data 575 | */ 576 | protected void WritePixels() 577 | { 578 | LZWEncoder encoder = 579 | new LZWEncoder(width, height, indexedPixels, colorDepth); 580 | encoder.Encode(ms); 581 | } 582 | 583 | /** 584 | * Write 16-bit value to output stream, LSB first 585 | */ 586 | protected void WriteShort(int value) 587 | { 588 | ms.WriteByte(Convert.ToByte(value & 0xff)); 589 | ms.WriteByte(Convert.ToByte((value >> 8) & 0xff)); 590 | } 591 | 592 | /** 593 | * Writes string to output stream 594 | */ 595 | protected void WriteString(String s) 596 | { 597 | char[] chars = s.ToCharArray(); 598 | for (int i = 0; i < chars.Length; i++) 599 | { 600 | ms.WriteByte((byte)chars[i]); 601 | } 602 | } 603 | } 604 | 605 | } -------------------------------------------------------------------------------- /Components/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | [assembly: AssemblyVersion("0.0.0.1")] 4 | [assembly: AssemblyTitle("Gif Animate")] 5 | [assembly: AssemblyDescription("")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCompany("")] 8 | [assembly: AssemblyProduct("")] 9 | [assembly: AssemblyCopyright("")] 10 | [assembly: AssemblyTrademark("")] 11 | [assembly: AssemblyCulture("")] 12 | [assembly: AssemblyDelaySign(false)] 13 | [assembly: AssemblyKeyFile("")] 14 | [assembly: AssemblyKeyName("")] 15 | -------------------------------------------------------------------------------- /Components/Components.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Local 5 | 7.10.3077 6 | 2.0 7 | {1C3A03CA-5E55-4CC4-9746-194A4188960A} 8 | Debug 9 | AnyCPU 10 | 11 | 12 | Gif.Components 13 | 14 | JScript 15 | Grid 16 | IE50 17 | false 18 | Library 19 | Gif.Components 20 | OnBuildSuccess 21 | 22 | 23 | 24 | v2.0 25 | 26 | 27 | 0.0 28 | 29 | 30 | bin\Debug\ 31 | false 32 | 285212672 33 | false 34 | 35 | DEBUG;TRACE 36 | 37 | true 38 | 4096 39 | false 40 | 41 | false 42 | false 43 | false 44 | false 45 | 4 46 | full 47 | prompt 48 | 49 | 50 | bin\Release\ 51 | false 52 | 285212672 53 | false 54 | 55 | TRACE 56 | 57 | false 58 | 4096 59 | false 60 | 61 | true 62 | false 63 | false 64 | false 65 | 4 66 | none 67 | prompt 68 | 69 | 70 | 71 | System 72 | 73 | 74 | System.Data 75 | 76 | 77 | System.Drawing 78 | 79 | 80 | System.XML 81 | 82 | 83 | 84 | 85 | Code 86 | 87 | 88 | Code 89 | 90 | 91 | Code 92 | 93 | 94 | Code 95 | 96 | 97 | Code 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /Components/Gif.resharperoptions: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Components/GifDecoder.cs: -------------------------------------------------------------------------------- 1 | #region .NET Disclaimer/Info 2 | //=============================================================================== 3 | // 4 | // gOODiDEA, uland.com 5 | //=============================================================================== 6 | // 7 | // $Header : $ 8 | // $Author : $ 9 | // $Date : $ 10 | // $Revision: $ 11 | // $History: $ 12 | // 13 | //=============================================================================== 14 | #endregion 15 | 16 | #region Java 17 | /** 18 | * Class GifDecoder - Decodes a GIF file into one or more frames. 19 | *
 20 |  * Example:
 21 |  *    GifDecoder d = new GifDecoder();
 22 |  *    d.read("sample.gif");
 23 |  *    int n = d.getFrameCount();
 24 |  *    for (int i = 0; i < n; i++) {
 25 |  *       BufferedImage frame = d.getFrame(i);  // frame i
 26 |  *       int t = d.getDelay(i);  // display duration of frame in milliseconds
 27 |  *       // do something with frame
 28 |  *    }
 29 |  * 
30 | * No copyright asserted on the source code of this class. May be used for 31 | * any purpose, however, refer to the Unisys LZW patent for any additional 32 | * restrictions. Please forward any corrections to kweiner@fmsware.com. 33 | * 34 | * @author Kevin Weiner, FM Software; LZW decoder adapted from John Cristy's ImageMagick. 35 | * @version 1.03 November 2003 36 | * 37 | */ 38 | #endregion 39 | using System; 40 | using System.Collections; 41 | using System.Drawing; 42 | using System.Drawing.Imaging; 43 | using System.IO; 44 | 45 | namespace Gif.Components 46 | { 47 | public class GifDecoder 48 | { 49 | 50 | /** 51 | * File read status: No errors. 52 | */ 53 | public static readonly int STATUS_OK = 0; 54 | 55 | /** 56 | * File read status: Error decoding file (may be partially decoded) 57 | */ 58 | public static readonly int STATUS_FORMAT_ERROR = 1; 59 | 60 | /** 61 | * File read status: Unable to open source. 62 | */ 63 | public static readonly int STATUS_OPEN_ERROR = 2; 64 | 65 | protected Stream inStream; 66 | protected int status; 67 | 68 | protected int width; // full image width 69 | protected int height; // full image height 70 | protected bool gctFlag; // global color table used 71 | protected int gctSize; // size of global color table 72 | protected int loopCount = 1; // iterations; 0 = repeat forever 73 | 74 | protected int[] gct; // global color table 75 | protected int[] lct; // local color table 76 | protected int[] act; // active color table 77 | 78 | protected int bgIndex; // background color index 79 | protected int bgColor; // background color 80 | protected int lastBgColor; // previous bg color 81 | protected int pixelAspect; // pixel aspect ratio 82 | 83 | protected bool lctFlag; // local color table flag 84 | protected bool interlace; // interlace flag 85 | protected int lctSize; // local color table size 86 | 87 | protected int ix, iy, iw, ih; // current image rectangle 88 | protected Rectangle lastRect; // last image rect 89 | protected Image image; // current frame 90 | protected Bitmap bitmap; 91 | protected Image lastImage; // previous frame 92 | 93 | protected byte[] block = new byte[256]; // current data block 94 | protected int blockSize = 0; // block size 95 | 96 | // last graphic control extension info 97 | protected int dispose = 0; 98 | // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev 99 | protected int lastDispose = 0; 100 | protected bool transparency = false; // use transparent color 101 | protected int delay = 0; // delay in milliseconds 102 | protected int transIndex; // transparent color index 103 | 104 | protected static readonly int MaxStackSize = 4096; 105 | // max decoder pixel stack size 106 | 107 | // LZW decoder working arrays 108 | protected short[] prefix; 109 | protected byte[] suffix; 110 | protected byte[] pixelStack; 111 | protected byte[] pixels; 112 | 113 | protected ArrayList frames; // frames read from current file 114 | protected int frameCount; 115 | 116 | public class GifFrame 117 | { 118 | public GifFrame( Image im, int del) 119 | { 120 | image = im; 121 | delay = del; 122 | } 123 | public Image image; 124 | public int delay; 125 | } 126 | 127 | /** 128 | * Gets display duration for specified frame. 129 | * 130 | * @param n int index of frame 131 | * @return delay in milliseconds 132 | */ 133 | public int GetDelay(int n) 134 | { 135 | // 136 | delay = -1; 137 | if ((n >= 0) && (n < frameCount)) 138 | { 139 | delay = ((GifFrame) frames[n]).delay; 140 | } 141 | return delay; 142 | } 143 | 144 | /** 145 | * Gets the number of frames read from file. 146 | * @return frame count 147 | */ 148 | public int GetFrameCount() 149 | { 150 | return frameCount; 151 | } 152 | 153 | /** 154 | * Gets the first (or only) image read. 155 | * 156 | * @return BufferedImage containing first frame, or null if none. 157 | */ 158 | public Image GetImage() 159 | { 160 | return GetFrame(0); 161 | } 162 | 163 | /** 164 | * Gets the "Netscape" iteration count, if any. 165 | * A count of 0 means repeat indefinitiely. 166 | * 167 | * @return iteration count if one was specified, else 1. 168 | */ 169 | public int GetLoopCount() 170 | { 171 | return loopCount; 172 | } 173 | 174 | /** 175 | * Creates new frame image from current data (and previous 176 | * frames as specified by their disposition codes). 177 | */ 178 | int [] GetPixels( Bitmap bitmap ) 179 | { 180 | int [] pixels = new int [ 3 * image.Width * image.Height ]; 181 | int count = 0; 182 | for (int th = 0; th < image.Height; th++) 183 | { 184 | for (int tw = 0; tw < image.Width; tw++) 185 | { 186 | Color color = bitmap.GetPixel(tw, th); 187 | pixels[count] = color.R; 188 | count++; 189 | pixels[count] = color.G; 190 | count++; 191 | pixels[count] = color.B; 192 | count++; 193 | } 194 | } 195 | return pixels; 196 | } 197 | 198 | void SetPixels( int [] pixels ) 199 | { 200 | int count = 0; 201 | for (int th = 0; th < image.Height; th++) 202 | { 203 | for (int tw = 0; tw < image.Width; tw++) 204 | { 205 | Color color = Color.FromArgb( pixels[count++] ); 206 | bitmap.SetPixel( tw, th, color ); 207 | } 208 | } 209 | } 210 | 211 | protected void SetPixels() 212 | { 213 | // expose destination image's pixels as int array 214 | // int[] dest = 215 | // (( int ) image.getRaster().getDataBuffer()).getData(); 216 | int[] dest = GetPixels( bitmap ); 217 | 218 | // fill in starting image contents based on last image's dispose code 219 | if (lastDispose > 0) 220 | { 221 | if (lastDispose == 3) 222 | { 223 | // use image before last 224 | int n = frameCount - 2; 225 | if (n > 0) 226 | { 227 | lastImage = GetFrame(n - 1); 228 | } 229 | else 230 | { 231 | lastImage = null; 232 | } 233 | } 234 | 235 | if (lastImage != null) 236 | { 237 | // int[] prev = 238 | // ((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData(); 239 | int[] prev = GetPixels( new Bitmap( lastImage ) ); 240 | Array.Copy(prev, 0, dest, 0, width * height); 241 | // copy pixels 242 | 243 | if (lastDispose == 2) 244 | { 245 | // fill last image rect area with background color 246 | Graphics g = Graphics.FromImage( image ); 247 | Color c = Color.Empty; 248 | if (transparency) 249 | { 250 | c = Color.FromArgb( 0, 0, 0, 0 ); // assume background is transparent 251 | } 252 | else 253 | { 254 | c = Color.FromArgb( lastBgColor ) ; 255 | // c = new Color(lastBgColor); // use given background color 256 | } 257 | Brush brush = new SolidBrush( c ); 258 | g.FillRectangle( brush, lastRect ); 259 | brush.Dispose(); 260 | g.Dispose(); 261 | } 262 | } 263 | } 264 | 265 | // copy each source line to the appropriate place in the destination 266 | int pass = 1; 267 | int inc = 8; 268 | int iline = 0; 269 | for (int i = 0; i < ih; i++) 270 | { 271 | int line = i; 272 | if (interlace) 273 | { 274 | if (iline >= ih) 275 | { 276 | pass++; 277 | switch (pass) 278 | { 279 | case 2 : 280 | iline = 4; 281 | break; 282 | case 3 : 283 | iline = 2; 284 | inc = 4; 285 | break; 286 | case 4 : 287 | iline = 1; 288 | inc = 2; 289 | break; 290 | } 291 | } 292 | line = iline; 293 | iline += inc; 294 | } 295 | line += iy; 296 | if (line < height) 297 | { 298 | int k = line * width; 299 | int dx = k + ix; // start of line in dest 300 | int dlim = dx + iw; // end of dest line 301 | if ((k + width) < dlim) 302 | { 303 | dlim = k + width; // past dest edge 304 | } 305 | int sx = i * iw; // start of line in source 306 | while (dx < dlim) 307 | { 308 | // map color and insert in destination 309 | int index = ((int) pixels[sx++]) & 0xff; 310 | int c = act[index]; 311 | if (c != 0) 312 | { 313 | dest[dx] = c; 314 | } 315 | dx++; 316 | } 317 | } 318 | } 319 | SetPixels( dest ); 320 | } 321 | 322 | /** 323 | * Gets the image contents of frame n. 324 | * 325 | * @return BufferedImage representation of frame, or null if n is invalid. 326 | */ 327 | public Image GetFrame(int n) 328 | { 329 | Image im = null; 330 | if ((n >= 0) && (n < frameCount)) 331 | { 332 | im = ((GifFrame) frames[n] ).image; 333 | } 334 | return im; 335 | } 336 | 337 | /** 338 | * Gets image size. 339 | * 340 | * @return GIF image dimensions 341 | */ 342 | public Size GetFrameSize() 343 | { 344 | return new Size(width, height); 345 | } 346 | 347 | /** 348 | * Reads GIF image from stream 349 | * 350 | * @param BufferedInputStream containing GIF file. 351 | * @return read status code (0 = no errors) 352 | */ 353 | public int Read( Stream inStream ) 354 | { 355 | Init(); 356 | if ( inStream != null) 357 | { 358 | this.inStream = inStream; 359 | ReadHeader(); 360 | if (!Error()) 361 | { 362 | ReadContents(); 363 | if (frameCount < 0) 364 | { 365 | status = STATUS_FORMAT_ERROR; 366 | } 367 | } 368 | inStream.Close(); 369 | } 370 | else 371 | { 372 | status = STATUS_OPEN_ERROR; 373 | } 374 | return status; 375 | } 376 | 377 | /** 378 | * Reads GIF file from specified file/URL source 379 | * (URL assumed if name contains ":/" or "file:") 380 | * 381 | * @param name String containing source 382 | * @return read status code (0 = no errors) 383 | */ 384 | public int Read(String name) 385 | { 386 | status = STATUS_OK; 387 | try 388 | { 389 | name = name.Trim().ToLower(); 390 | status = Read( new FileInfo( name ).OpenRead() ); 391 | } 392 | catch (IOException) 393 | { 394 | status = STATUS_OPEN_ERROR; 395 | } 396 | 397 | return status; 398 | } 399 | 400 | /** 401 | * Decodes LZW image data into pixel array. 402 | * Adapted from John Cristy's ImageMagick. 403 | */ 404 | protected void DecodeImageData() 405 | { 406 | int NullCode = -1; 407 | int npix = iw * ih; 408 | int available, 409 | clear, 410 | code_mask, 411 | code_size, 412 | end_of_information, 413 | in_code, 414 | old_code, 415 | bits, 416 | code, 417 | count, 418 | i, 419 | datum, 420 | data_size, 421 | first, 422 | top, 423 | bi, 424 | pi; 425 | 426 | if ((pixels == null) || (pixels.Length < npix)) 427 | { 428 | pixels = new byte[npix]; // allocate new pixel array 429 | } 430 | if (prefix == null) prefix = new short[MaxStackSize]; 431 | if (suffix == null) suffix = new byte[MaxStackSize]; 432 | if (pixelStack == null) pixelStack = new byte[MaxStackSize + 1]; 433 | 434 | // Initialize GIF data stream decoder. 435 | 436 | data_size = Read(); 437 | clear = 1 << data_size; 438 | end_of_information = clear + 1; 439 | available = clear + 2; 440 | old_code = NullCode; 441 | code_size = data_size + 1; 442 | code_mask = (1 << code_size) - 1; 443 | for (code = 0; code < clear; code++) 444 | { 445 | prefix[code] = 0; 446 | suffix[code] = (byte) code; 447 | } 448 | 449 | // Decode GIF pixel stream. 450 | 451 | datum = bits = count = first = top = pi = bi = 0; 452 | 453 | for (i = 0; i < npix;) 454 | { 455 | if (top == 0) 456 | { 457 | if (bits < code_size) 458 | { 459 | // Load bytes until there are enough bits for a code. 460 | if (count == 0) 461 | { 462 | // Read a new data block. 463 | count = ReadBlock(); 464 | if (count <= 0) 465 | break; 466 | bi = 0; 467 | } 468 | datum += (((int) block[bi]) & 0xff) << bits; 469 | bits += 8; 470 | bi++; 471 | count--; 472 | continue; 473 | } 474 | 475 | // Get the next code. 476 | 477 | code = datum & code_mask; 478 | datum >>= code_size; 479 | bits -= code_size; 480 | 481 | // Interpret the code 482 | 483 | if ((code > available) || (code == end_of_information)) 484 | break; 485 | if (code == clear) 486 | { 487 | // Reset decoder. 488 | code_size = data_size + 1; 489 | code_mask = (1 << code_size) - 1; 490 | available = clear + 2; 491 | old_code = NullCode; 492 | continue; 493 | } 494 | if (old_code == NullCode) 495 | { 496 | pixelStack[top++] = suffix[code]; 497 | old_code = code; 498 | first = code; 499 | continue; 500 | } 501 | in_code = code; 502 | if (code == available) 503 | { 504 | pixelStack[top++] = (byte) first; 505 | code = old_code; 506 | } 507 | while (code > clear) 508 | { 509 | pixelStack[top++] = suffix[code]; 510 | code = prefix[code]; 511 | } 512 | first = ((int) suffix[code]) & 0xff; 513 | 514 | // Add a new string to the string table, 515 | 516 | if (available >= MaxStackSize) 517 | break; 518 | pixelStack[top++] = (byte) first; 519 | prefix[available] = (short) old_code; 520 | suffix[available] = (byte) first; 521 | available++; 522 | if (((available & code_mask) == 0) 523 | && (available < MaxStackSize)) 524 | { 525 | code_size++; 526 | code_mask += available; 527 | } 528 | old_code = in_code; 529 | } 530 | 531 | // Pop a pixel off the pixel stack. 532 | 533 | top--; 534 | pixels[pi++] = pixelStack[top]; 535 | i++; 536 | } 537 | 538 | for (i = pi; i < npix; i++) 539 | { 540 | pixels[i] = 0; // clear missing pixels 541 | } 542 | 543 | } 544 | 545 | /** 546 | * Returns true if an error was encountered during reading/decoding 547 | */ 548 | protected bool Error() 549 | { 550 | return status != STATUS_OK; 551 | } 552 | 553 | /** 554 | * Initializes or re-initializes reader 555 | */ 556 | protected void Init() 557 | { 558 | status = STATUS_OK; 559 | frameCount = 0; 560 | frames = new ArrayList(); 561 | gct = null; 562 | lct = null; 563 | } 564 | 565 | /** 566 | * Reads a single byte from the input stream. 567 | */ 568 | protected int Read() 569 | { 570 | int curByte = 0; 571 | try 572 | { 573 | curByte = inStream.ReadByte(); 574 | } 575 | catch (IOException) 576 | { 577 | status = STATUS_FORMAT_ERROR; 578 | } 579 | return curByte; 580 | } 581 | 582 | /** 583 | * Reads next variable length block from input. 584 | * 585 | * @return number of bytes stored in "buffer" 586 | */ 587 | protected int ReadBlock() 588 | { 589 | blockSize = Read(); 590 | int n = 0; 591 | if (blockSize > 0) 592 | { 593 | try 594 | { 595 | int count = 0; 596 | while (n < blockSize) 597 | { 598 | count = inStream.Read(block, n, blockSize - n); 599 | if (count == -1) 600 | break; 601 | n += count; 602 | } 603 | } 604 | catch (IOException) 605 | { 606 | } 607 | 608 | if (n < blockSize) 609 | { 610 | status = STATUS_FORMAT_ERROR; 611 | } 612 | } 613 | return n; 614 | } 615 | 616 | /** 617 | * Reads color table as 256 RGB integer values 618 | * 619 | * @param ncolors int number of colors to read 620 | * @return int array containing 256 colors (packed ARGB with full alpha) 621 | */ 622 | protected int[] ReadColorTable(int ncolors) 623 | { 624 | int nbytes = 3 * ncolors; 625 | int[] tab = null; 626 | byte[] c = new byte[nbytes]; 627 | int n = 0; 628 | try 629 | { 630 | n = inStream.Read(c, 0, c.Length ); 631 | } 632 | catch (IOException) 633 | { 634 | } 635 | if (n < nbytes) 636 | { 637 | status = STATUS_FORMAT_ERROR; 638 | } 639 | else 640 | { 641 | tab = new int[256]; // max size to avoid bounds checks 642 | int i = 0; 643 | int j = 0; 644 | while (i < ncolors) 645 | { 646 | int r = ((int) c[j++]) & 0xff; 647 | int g = ((int) c[j++]) & 0xff; 648 | int b = ((int) c[j++]) & 0xff; 649 | tab[i++] = (int) (0xff000000 | (r << 16) | (g << 8) | b ); 650 | } 651 | } 652 | return tab; 653 | } 654 | 655 | /** 656 | * Main file parser. Reads GIF content blocks. 657 | */ 658 | protected void ReadContents() 659 | { 660 | // read GIF file content blocks 661 | bool done = false; 662 | while (!(done || Error())) 663 | { 664 | int code = Read(); 665 | switch (code) 666 | { 667 | 668 | case 0x2C : // image separator 669 | ReadImage(); 670 | break; 671 | 672 | case 0x21 : // extension 673 | code = Read(); 674 | switch (code) 675 | { 676 | case 0xf9 : // graphics control extension 677 | ReadGraphicControlExt(); 678 | break; 679 | 680 | case 0xff : // application extension 681 | ReadBlock(); 682 | String app = ""; 683 | for (int i = 0; i < 11; i++) 684 | { 685 | app += (char) block[i]; 686 | } 687 | if (app.Equals("NETSCAPE2.0")) 688 | { 689 | ReadNetscapeExt(); 690 | } 691 | else 692 | Skip(); // don't care 693 | break; 694 | 695 | default : // uninteresting extension 696 | Skip(); 697 | break; 698 | } 699 | break; 700 | 701 | case 0x3b : // terminator 702 | done = true; 703 | break; 704 | 705 | case 0x00 : // bad byte, but keep going and see what happens 706 | break; 707 | 708 | default : 709 | status = STATUS_FORMAT_ERROR; 710 | break; 711 | } 712 | } 713 | } 714 | 715 | /** 716 | * Reads Graphics Control Extension values 717 | */ 718 | protected void ReadGraphicControlExt() 719 | { 720 | Read(); // block size 721 | int packed = Read(); // packed fields 722 | dispose = (packed & 0x1c) >> 2; // disposal method 723 | if (dispose == 0) 724 | { 725 | dispose = 1; // elect to keep old image if discretionary 726 | } 727 | transparency = (packed & 1) != 0; 728 | delay = ReadShort() * 10; // delay in milliseconds 729 | transIndex = Read(); // transparent color index 730 | Read(); // block terminator 731 | } 732 | 733 | /** 734 | * Reads GIF file header information. 735 | */ 736 | protected void ReadHeader() 737 | { 738 | String id = ""; 739 | for (int i = 0; i < 6; i++) 740 | { 741 | id += (char) Read(); 742 | } 743 | if (!id.StartsWith("GIF")) 744 | { 745 | status = STATUS_FORMAT_ERROR; 746 | return; 747 | } 748 | 749 | ReadLSD(); 750 | if (gctFlag && !Error()) 751 | { 752 | gct = ReadColorTable(gctSize); 753 | bgColor = gct[bgIndex]; 754 | } 755 | } 756 | 757 | /** 758 | * Reads next frame image 759 | */ 760 | protected void ReadImage() 761 | { 762 | ix = ReadShort(); // (sub)image position & size 763 | iy = ReadShort(); 764 | iw = ReadShort(); 765 | ih = ReadShort(); 766 | 767 | int packed = Read(); 768 | lctFlag = (packed & 0x80) != 0; // 1 - local color table flag 769 | interlace = (packed & 0x40) != 0; // 2 - interlace flag 770 | // 3 - sort flag 771 | // 4-5 - reserved 772 | lctSize = 2 << (packed & 7); // 6-8 - local color table size 773 | 774 | if (lctFlag) 775 | { 776 | lct = ReadColorTable(lctSize); // read table 777 | act = lct; // make local table active 778 | } 779 | else 780 | { 781 | act = gct; // make global table active 782 | if (bgIndex == transIndex) 783 | bgColor = 0; 784 | } 785 | int save = 0; 786 | if (transparency) 787 | { 788 | save = act[transIndex]; 789 | act[transIndex] = 0; // set transparent color if specified 790 | } 791 | 792 | if (act == null) 793 | { 794 | status = STATUS_FORMAT_ERROR; // no color table defined 795 | } 796 | 797 | if (Error()) return; 798 | 799 | DecodeImageData(); // decode pixel data 800 | Skip(); 801 | 802 | if (Error()) return; 803 | 804 | frameCount++; 805 | 806 | // create new image to receive frame data 807 | // image = 808 | // new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE); 809 | 810 | bitmap = new Bitmap( width, height ); 811 | image = bitmap; 812 | SetPixels(); // transfer pixel data to image 813 | 814 | frames.Add(new GifFrame(bitmap, delay)); // add image to frame list 815 | 816 | if (transparency) 817 | { 818 | act[transIndex] = save; 819 | } 820 | ResetFrame(); 821 | 822 | } 823 | 824 | /** 825 | * Reads Logical Screen Descriptor 826 | */ 827 | protected void ReadLSD() 828 | { 829 | 830 | // logical screen size 831 | width = ReadShort(); 832 | height = ReadShort(); 833 | 834 | // packed fields 835 | int packed = Read(); 836 | gctFlag = (packed & 0x80) != 0; // 1 : global color table flag 837 | // 2-4 : color resolution 838 | // 5 : gct sort flag 839 | gctSize = 2 << (packed & 7); // 6-8 : gct size 840 | 841 | bgIndex = Read(); // background color index 842 | pixelAspect = Read(); // pixel aspect ratio 843 | } 844 | 845 | /** 846 | * Reads Netscape extenstion to obtain iteration count 847 | */ 848 | protected void ReadNetscapeExt() 849 | { 850 | do 851 | { 852 | ReadBlock(); 853 | if (block[0] == 1) 854 | { 855 | // loop count sub-block 856 | int b1 = ((int) block[1]) & 0xff; 857 | int b2 = ((int) block[2]) & 0xff; 858 | loopCount = (b2 << 8) | b1; 859 | } 860 | } while ((blockSize > 0) && !Error()); 861 | } 862 | 863 | /** 864 | * Reads next 16-bit value, LSB first 865 | */ 866 | protected int ReadShort() 867 | { 868 | // read 16-bit value, LSB first 869 | return Read() | (Read() << 8); 870 | } 871 | 872 | /** 873 | * Resets frame state for reading next image. 874 | */ 875 | protected void ResetFrame() 876 | { 877 | lastDispose = dispose; 878 | lastRect = new Rectangle(ix, iy, iw, ih); 879 | lastImage = image; 880 | lastBgColor = bgColor; 881 | lct = null; 882 | } 883 | 884 | /** 885 | * Skips variable length blocks up to and including 886 | * next zero length block. 887 | */ 888 | protected void Skip() 889 | { 890 | do 891 | { 892 | ReadBlock(); 893 | } while ((blockSize > 0) && !Error()); 894 | } 895 | } 896 | } 897 | -------------------------------------------------------------------------------- /Components/LZWEncoder.cs: -------------------------------------------------------------------------------- 1 | #region .NET Disclaimer/Info 2 | //=============================================================================== 3 | // 4 | // gOODiDEA, uland.com 5 | //=============================================================================== 6 | // 7 | // $Header : $ 8 | // $Author : $ 9 | // $Date : $ 10 | // $Revision: $ 11 | // $History: $ 12 | // 13 | //=============================================================================== 14 | #endregion 15 | 16 | #region Java 17 | //============================================================================== 18 | // Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott. 19 | // K Weiner 12/00 20 | #endregion 21 | using System; 22 | using System.IO; 23 | 24 | namespace Gif.Components 25 | { 26 | public class LZWEncoder 27 | { 28 | 29 | private static readonly int EOF = -1; 30 | 31 | private int imgW, imgH; 32 | private byte[] pixAry; 33 | private int initCodeSize; 34 | private int remaining; 35 | private int curPixel; 36 | 37 | // GIFCOMPR.C - GIF Image compression routines 38 | // 39 | // Lempel-Ziv compression based on 'compress'. GIF modifications by 40 | // David Rowley (mgardi@watdcsu.waterloo.edu) 41 | 42 | // General DEFINEs 43 | 44 | static readonly int BITS = 12; 45 | 46 | static readonly int HSIZE = 5003; // 80% occupancy 47 | 48 | // GIF Image compression - modified 'compress' 49 | // 50 | // Based on: compress.c - File compression ala IEEE Computer, June 1984. 51 | // 52 | // By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) 53 | // Jim McKie (decvax!mcvax!jim) 54 | // Steve Davies (decvax!vax135!petsd!peora!srd) 55 | // Ken Turkowski (decvax!decwrl!turtlevax!ken) 56 | // James A. Woods (decvax!ihnp4!ames!jaw) 57 | // Joe Orost (decvax!vax135!petsd!joe) 58 | 59 | int n_bits; // number of bits/code 60 | int maxbits = BITS; // user settable max # bits/code 61 | int maxcode; // maximum code, given n_bits 62 | int maxmaxcode = 1 << BITS; // should NEVER generate this code 63 | 64 | int[] htab = new int[HSIZE]; 65 | int[] codetab = new int[HSIZE]; 66 | 67 | int hsize = HSIZE; // for dynamic table sizing 68 | 69 | int free_ent = 0; // first unused entry 70 | 71 | // block compression parameters -- after all codes are used up, 72 | // and compression rate changes, start over. 73 | bool clear_flg = false; 74 | 75 | // Algorithm: use open addressing double hashing (no chaining) on the 76 | // prefix code / next character combination. We do a variant of Knuth's 77 | // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime 78 | // secondary probe. Here, the modular division first probe is gives way 79 | // to a faster exclusive-or manipulation. Also do block compression with 80 | // an adaptive reset, whereby the code table is cleared when the compression 81 | // ratio decreases, but after the table fills. The variable-length output 82 | // codes are re-sized at this point, and a special CLEAR code is generated 83 | // for the decompressor. Late addition: construct the table according to 84 | // file size for noticeable speed improvement on small files. Please direct 85 | // questions about this implementation to ames!jaw. 86 | 87 | int g_init_bits; 88 | 89 | int ClearCode; 90 | int EOFCode; 91 | 92 | // output 93 | // 94 | // Output the given code. 95 | // Inputs: 96 | // code: A n_bits-bit integer. If == -1, then EOF. This assumes 97 | // that n_bits =< wordsize - 1. 98 | // Outputs: 99 | // Outputs code to the file. 100 | // Assumptions: 101 | // Chars are 8 bits long. 102 | // Algorithm: 103 | // Maintain a BITS character long buffer (so that 8 codes will 104 | // fit in it exactly). Use the VAX insv instruction to insert each 105 | // code in turn. When the buffer fills up empty it and start over. 106 | 107 | int cur_accum = 0; 108 | int cur_bits = 0; 109 | 110 | int [] masks = 111 | { 112 | 0x0000, 113 | 0x0001, 114 | 0x0003, 115 | 0x0007, 116 | 0x000F, 117 | 0x001F, 118 | 0x003F, 119 | 0x007F, 120 | 0x00FF, 121 | 0x01FF, 122 | 0x03FF, 123 | 0x07FF, 124 | 0x0FFF, 125 | 0x1FFF, 126 | 0x3FFF, 127 | 0x7FFF, 128 | 0xFFFF }; 129 | 130 | // Number of characters so far in this 'packet' 131 | int a_count; 132 | 133 | // Define the storage for the packet accumulator 134 | byte[] accum = new byte[256]; 135 | 136 | //---------------------------------------------------------------------------- 137 | public LZWEncoder(int width, int height, byte[] pixels, int color_depth) 138 | { 139 | imgW = width; 140 | imgH = height; 141 | pixAry = pixels; 142 | initCodeSize = Math.Max(2, color_depth); 143 | } 144 | 145 | // Add a character to the end of the current packet, and if it is 254 146 | // characters, flush the packet to disk. 147 | void Add(byte c, Stream outs) 148 | { 149 | accum[a_count++] = c; 150 | if (a_count >= 254) 151 | Flush(outs); 152 | } 153 | 154 | // Clear out the hash table 155 | 156 | // table clear for block compress 157 | void ClearTable(Stream outs) 158 | { 159 | ResetCodeTable(hsize); 160 | free_ent = ClearCode + 2; 161 | clear_flg = true; 162 | 163 | Output(ClearCode, outs); 164 | } 165 | 166 | // reset code table 167 | void ResetCodeTable(int hsize) 168 | { 169 | for (int i = 0; i < hsize; ++i) 170 | htab[i] = -1; 171 | } 172 | 173 | void Compress(int init_bits, Stream outs) 174 | { 175 | int fcode; 176 | int i /* = 0 */; 177 | int c; 178 | int ent; 179 | int disp; 180 | int hsize_reg; 181 | int hshift; 182 | 183 | // Set up the globals: g_init_bits - initial number of bits 184 | g_init_bits = init_bits; 185 | 186 | // Set up the necessary values 187 | clear_flg = false; 188 | n_bits = g_init_bits; 189 | maxcode = MaxCode(n_bits); 190 | 191 | ClearCode = 1 << (init_bits - 1); 192 | EOFCode = ClearCode + 1; 193 | free_ent = ClearCode + 2; 194 | 195 | a_count = 0; // clear packet 196 | 197 | ent = NextPixel(); 198 | 199 | hshift = 0; 200 | for (fcode = hsize; fcode < 65536; fcode *= 2) 201 | ++hshift; 202 | hshift = 8 - hshift; // set hash code range bound 203 | 204 | hsize_reg = hsize; 205 | ResetCodeTable(hsize_reg); // clear hash table 206 | 207 | Output(ClearCode, outs); 208 | 209 | outer_loop : while ((c = NextPixel()) != EOF) 210 | { 211 | fcode = (c << maxbits) + ent; 212 | i = (c << hshift) ^ ent; // xor hashing 213 | 214 | if (htab[i] == fcode) 215 | { 216 | ent = codetab[i]; 217 | continue; 218 | } 219 | else if (htab[i] >= 0) // non-empty slot 220 | { 221 | disp = hsize_reg - i; // secondary hash (after G. Knott) 222 | if (i == 0) 223 | disp = 1; 224 | do 225 | { 226 | if ((i -= disp) < 0) 227 | i += hsize_reg; 228 | 229 | if (htab[i] == fcode) 230 | { 231 | ent = codetab[i]; 232 | goto outer_loop; 233 | } 234 | } while (htab[i] >= 0); 235 | } 236 | Output(ent, outs); 237 | ent = c; 238 | if (free_ent < maxmaxcode) 239 | { 240 | codetab[i] = free_ent++; // code -> hashtable 241 | htab[i] = fcode; 242 | } 243 | else 244 | ClearTable(outs); 245 | } 246 | // Put out the final code. 247 | Output(ent, outs); 248 | Output(EOFCode, outs); 249 | } 250 | 251 | //---------------------------------------------------------------------------- 252 | public void Encode( Stream os) 253 | { 254 | os.WriteByte( Convert.ToByte( initCodeSize) ); // write "initial code size" byte 255 | 256 | remaining = imgW * imgH; // reset navigation variables 257 | curPixel = 0; 258 | 259 | Compress(initCodeSize + 1, os); // compress and write the pixel data 260 | 261 | os.WriteByte(0); // write block terminator 262 | } 263 | 264 | // Flush the packet to disk, and reset the accumulator 265 | void Flush(Stream outs) 266 | { 267 | if (a_count > 0) 268 | { 269 | outs.WriteByte( Convert.ToByte( a_count )); 270 | outs.Write(accum, 0, a_count); 271 | a_count = 0; 272 | } 273 | } 274 | 275 | int MaxCode(int n_bits) 276 | { 277 | return (1 << n_bits) - 1; 278 | } 279 | 280 | //---------------------------------------------------------------------------- 281 | // Return the next pixel from the image 282 | //---------------------------------------------------------------------------- 283 | private int NextPixel() 284 | { 285 | int upperBound = pixAry.GetUpperBound(0); 286 | 287 | return (curPixel <= upperBound) ? (pixAry[curPixel++] & 0xff) : EOF; 288 | } 289 | 290 | void Output(int code, Stream outs) 291 | { 292 | cur_accum &= masks[cur_bits]; 293 | 294 | if (cur_bits > 0) 295 | cur_accum |= (code << cur_bits); 296 | else 297 | cur_accum = code; 298 | 299 | cur_bits += n_bits; 300 | 301 | while (cur_bits >= 8) 302 | { 303 | Add((byte) (cur_accum & 0xff), outs); 304 | cur_accum >>= 8; 305 | cur_bits -= 8; 306 | } 307 | 308 | // If the next entry is going to be too big for the code size, 309 | // then increase it, if possible. 310 | if (free_ent > maxcode || clear_flg) 311 | { 312 | if (clear_flg) 313 | { 314 | maxcode = MaxCode(n_bits = g_init_bits); 315 | clear_flg = false; 316 | } 317 | else 318 | { 319 | ++n_bits; 320 | if (n_bits == maxbits) 321 | maxcode = maxmaxcode; 322 | else 323 | maxcode = MaxCode(n_bits); 324 | } 325 | } 326 | 327 | if (code == EOFCode) 328 | { 329 | // At EOF, write the rest of the buffer. 330 | while (cur_bits > 0) 331 | { 332 | Add((byte) (cur_accum & 0xff), outs); 333 | cur_accum >>= 8; 334 | cur_bits -= 8; 335 | } 336 | 337 | Flush(outs); 338 | } 339 | } 340 | } 341 | } -------------------------------------------------------------------------------- /Components/NeuQuant.cs: -------------------------------------------------------------------------------- 1 | #region .NET Disclaimer/Info 2 | //=============================================================================== 3 | // 4 | // gOODiDEA, uland.com 5 | //=============================================================================== 6 | // 7 | // $Header : $ 8 | // $Author : $ 9 | // $Date : $ 10 | // $Revision: $ 11 | // $History: $ 12 | // 13 | //=============================================================================== 14 | #endregion 15 | 16 | #region Java 17 | /* NeuQuant Neural-Net Quantization Algorithm 18 | * ------------------------------------------ 19 | * 20 | * Copyright (c) 1994 Anthony Dekker 21 | * 22 | * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. 23 | * See "Kohonen neural networks for optimal colour quantization" 24 | * in "Network: Computation in Neural Systems" Vol. 5 (1994) pp 351-367. 25 | * for a discussion of the algorithm. 26 | * 27 | * Any party obtaining a copy of these files from the author, directly or 28 | * indirectly, is granted, free of charge, a full and unrestricted irrevocable, 29 | * world-wide, paid up, royalty-free, nonexclusive right and license to deal 30 | * in this software and documentation files (the "Software"), including without 31 | * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 32 | * and/or sell copies of the Software, and to permit persons who receive 33 | * copies from any such party to do so, with the only requirement being 34 | * that this copyright notice remain intact. 35 | */ 36 | 37 | // Ported to Java 12/00 K Weiner 38 | #endregion 39 | 40 | using System; 41 | 42 | namespace Gif.Components 43 | { 44 | public class NeuQuant 45 | { 46 | protected static readonly int netsize = 256; /* number of colours used */ 47 | /* four primes near 500 - assume no image has a length so large */ 48 | /* that it is divisible by all four primes */ 49 | protected static readonly int prime1 = 499; 50 | protected static readonly int prime2 = 491; 51 | protected static readonly int prime3 = 487; 52 | protected static readonly int prime4 = 503; 53 | protected static readonly int minpicturebytes = ( 3 * prime4 ); 54 | /* minimum size for input image */ 55 | /* Program Skeleton 56 | ---------------- 57 | [select samplefac in range 1..30] 58 | [read image from input file] 59 | pic = (unsigned char*) malloc(3*width*height); 60 | initnet(pic,3*width*height,samplefac); 61 | learn(); 62 | unbiasnet(); 63 | [write output image header, using writecolourmap(f)] 64 | inxbuild(); 65 | write output image using inxsearch(b,g,r) */ 66 | 67 | /* Network Definitions 68 | ------------------- */ 69 | protected static readonly int maxnetpos = (netsize - 1); 70 | protected static readonly int netbiasshift = 4; /* bias for colour values */ 71 | protected static readonly int ncycles = 100; /* no. of learning cycles */ 72 | 73 | /* defs for freq and bias */ 74 | protected static readonly int intbiasshift = 16; /* bias for fractions */ 75 | protected static readonly int intbias = (((int) 1) << intbiasshift); 76 | protected static readonly int gammashift = 10; /* gamma = 1024 */ 77 | protected static readonly int gamma = (((int) 1) << gammashift); 78 | protected static readonly int betashift = 10; 79 | protected static readonly int beta = (intbias >> betashift); /* beta = 1/1024 */ 80 | protected static readonly int betagamma = 81 | (intbias << (gammashift - betashift)); 82 | 83 | /* defs for decreasing radius factor */ 84 | protected static readonly int initrad = (netsize >> 3); /* for 256 cols, radius starts */ 85 | protected static readonly int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */ 86 | protected static readonly int radiusbias = (((int) 1) << radiusbiasshift); 87 | protected static readonly int initradius = (initrad * radiusbias); /* and decreases by a */ 88 | protected static readonly int radiusdec = 30; /* factor of 1/30 each cycle */ 89 | 90 | /* defs for decreasing alpha factor */ 91 | protected static readonly int alphabiasshift = 10; /* alpha starts at 1.0 */ 92 | protected static readonly int initalpha = (((int) 1) << alphabiasshift); 93 | 94 | protected int alphadec; /* biased by 10 bits */ 95 | 96 | /* radbias and alpharadbias used for radpower calculation */ 97 | protected static readonly int radbiasshift = 8; 98 | protected static readonly int radbias = (((int) 1) << radbiasshift); 99 | protected static readonly int alpharadbshift = (alphabiasshift + radbiasshift); 100 | protected static readonly int alpharadbias = (((int) 1) << alpharadbshift); 101 | 102 | /* Types and Global Variables 103 | -------------------------- */ 104 | 105 | protected byte[] thepicture; /* the input image itself */ 106 | protected int lengthcount; /* lengthcount = H*W*3 */ 107 | 108 | protected int samplefac; /* sampling factor 1..30 */ 109 | 110 | // typedef int pixel[4]; /* BGRc */ 111 | protected int[][] network; /* the network itself - [netsize][4] */ 112 | 113 | protected int[] netindex = new int[256]; 114 | /* for network lookup - really 256 */ 115 | 116 | protected int[] bias = new int[netsize]; 117 | /* bias and freq arrays for learning */ 118 | protected int[] freq = new int[netsize]; 119 | protected int[] radpower = new int[initrad]; 120 | /* radpower for precomputation */ 121 | 122 | /* Initialise network in range (0,0,0) to (255,255,255) and set parameters 123 | ----------------------------------------------------------------------- */ 124 | public NeuQuant(byte[] thepic, int len, int sample) 125 | { 126 | 127 | int i; 128 | int[] p; 129 | 130 | thepicture = thepic; 131 | lengthcount = len; 132 | samplefac = sample; 133 | 134 | network = new int[netsize][]; 135 | for (i = 0; i < netsize; i++) 136 | { 137 | network[i] = new int[4]; 138 | p = network[i]; 139 | p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / netsize; 140 | freq[i] = intbias / netsize; /* 1/netsize */ 141 | bias[i] = 0; 142 | } 143 | } 144 | 145 | public byte[] ColorMap() 146 | { 147 | byte[] map = new byte[3 * netsize]; 148 | int[] index = new int[netsize]; 149 | for (int i = 0; i < netsize; i++) 150 | index[network[i][3]] = i; 151 | int k = 0; 152 | for (int i = 0; i < netsize; i++) 153 | { 154 | int j = index[i]; 155 | map[k++] = (byte) (network[j][0]); 156 | map[k++] = (byte) (network[j][1]); 157 | map[k++] = (byte) (network[j][2]); 158 | } 159 | return map; 160 | } 161 | 162 | /* Insertion sort of network and building of netindex[0..255] (to do after unbias) 163 | ------------------------------------------------------------------------------- */ 164 | public void Inxbuild() 165 | { 166 | 167 | int i, j, smallpos, smallval; 168 | int[] p; 169 | int[] q; 170 | int previouscol, startpos; 171 | 172 | previouscol = 0; 173 | startpos = 0; 174 | for (i = 0; i < netsize; i++) 175 | { 176 | p = network[i]; 177 | smallpos = i; 178 | smallval = p[1]; /* index on g */ 179 | /* find smallest in i..netsize-1 */ 180 | for (j = i + 1; j < netsize; j++) 181 | { 182 | q = network[j]; 183 | if (q[1] < smallval) 184 | { /* index on g */ 185 | smallpos = j; 186 | smallval = q[1]; /* index on g */ 187 | } 188 | } 189 | q = network[smallpos]; 190 | /* swap p (i) and q (smallpos) entries */ 191 | if (i != smallpos) 192 | { 193 | j = q[0]; 194 | q[0] = p[0]; 195 | p[0] = j; 196 | j = q[1]; 197 | q[1] = p[1]; 198 | p[1] = j; 199 | j = q[2]; 200 | q[2] = p[2]; 201 | p[2] = j; 202 | j = q[3]; 203 | q[3] = p[3]; 204 | p[3] = j; 205 | } 206 | /* smallval entry is now in position i */ 207 | if (smallval != previouscol) 208 | { 209 | netindex[previouscol] = (startpos + i) >> 1; 210 | for (j = previouscol + 1; j < smallval; j++) 211 | netindex[j] = i; 212 | previouscol = smallval; 213 | startpos = i; 214 | } 215 | } 216 | netindex[previouscol] = (startpos + maxnetpos) >> 1; 217 | for (j = previouscol + 1; j < 256; j++) 218 | netindex[j] = maxnetpos; /* really 256 */ 219 | } 220 | 221 | /* Main Learning Loop 222 | ------------------ */ 223 | public void Learn() 224 | { 225 | 226 | int i, j, b, g, r; 227 | int radius, rad, alpha, step, delta, samplepixels; 228 | byte[] p; 229 | int pix, lim; 230 | 231 | if (lengthcount < minpicturebytes) 232 | samplefac = 1; 233 | alphadec = 30 + ((samplefac - 1) / 3); 234 | p = thepicture; 235 | pix = 0; 236 | lim = lengthcount; 237 | samplepixels = lengthcount / (3 * samplefac); 238 | delta = samplepixels / ncycles; 239 | alpha = initalpha; 240 | radius = initradius; 241 | 242 | rad = radius >> radiusbiasshift; 243 | if (rad <= 1) 244 | rad = 0; 245 | for (i = 0; i < rad; i++) 246 | radpower[i] = 247 | alpha * (((rad * rad - i * i) * radbias) / (rad * rad)); 248 | 249 | //fprintf(stderr,"beginning 1D learning: initial radius=%d\n", rad); 250 | 251 | if (lengthcount < minpicturebytes) 252 | step = 3; 253 | else if ((lengthcount % prime1) != 0) 254 | step = 3 * prime1; 255 | else 256 | { 257 | if ((lengthcount % prime2) != 0) 258 | step = 3 * prime2; 259 | else 260 | { 261 | if ((lengthcount % prime3) != 0) 262 | step = 3 * prime3; 263 | else 264 | step = 3 * prime4; 265 | } 266 | } 267 | 268 | i = 0; 269 | while (i < samplepixels) 270 | { 271 | b = (p[pix + 0] & 0xff) << netbiasshift; 272 | g = (p[pix + 1] & 0xff) << netbiasshift; 273 | r = (p[pix + 2] & 0xff) << netbiasshift; 274 | j = Contest(b, g, r); 275 | 276 | Altersingle(alpha, j, b, g, r); 277 | if (rad != 0) 278 | Alterneigh(rad, j, b, g, r); /* alter neighbours */ 279 | 280 | pix += step; 281 | if (pix >= lim) 282 | pix -= lengthcount; 283 | 284 | i++; 285 | if (delta == 0) 286 | delta = 1; 287 | if (i % delta == 0) 288 | { 289 | alpha -= alpha / alphadec; 290 | radius -= radius / radiusdec; 291 | rad = radius >> radiusbiasshift; 292 | if (rad <= 1) 293 | rad = 0; 294 | for (j = 0; j < rad; j++) 295 | radpower[j] = 296 | alpha * (((rad * rad - j * j) * radbias) / (rad * rad)); 297 | } 298 | } 299 | //fprintf(stderr,"finished 1D learning: readonly alpha=%f !\n",((float)alpha)/initalpha); 300 | } 301 | 302 | /* Search for BGR values 0..255 (after net is unbiased) and return colour index 303 | ---------------------------------------------------------------------------- */ 304 | public int Map(int b, int g, int r) 305 | { 306 | 307 | int i, j, dist, a, bestd; 308 | int[] p; 309 | int best; 310 | 311 | bestd = 1000; /* biggest possible dist is 256*3 */ 312 | best = -1; 313 | i = netindex[g]; /* index on g */ 314 | j = i - 1; /* start at netindex[g] and work outwards */ 315 | 316 | while ((i < netsize) || (j >= 0)) 317 | { 318 | if (i < netsize) 319 | { 320 | p = network[i]; 321 | dist = p[1] - g; /* inx key */ 322 | if (dist >= bestd) 323 | i = netsize; /* stop iter */ 324 | else 325 | { 326 | i++; 327 | if (dist < 0) 328 | dist = -dist; 329 | a = p[0] - b; 330 | if (a < 0) 331 | a = -a; 332 | dist += a; 333 | if (dist < bestd) 334 | { 335 | a = p[2] - r; 336 | if (a < 0) 337 | a = -a; 338 | dist += a; 339 | if (dist < bestd) 340 | { 341 | bestd = dist; 342 | best = p[3]; 343 | } 344 | } 345 | } 346 | } 347 | if (j >= 0) 348 | { 349 | p = network[j]; 350 | dist = g - p[1]; /* inx key - reverse dif */ 351 | if (dist >= bestd) 352 | j = -1; /* stop iter */ 353 | else 354 | { 355 | j--; 356 | if (dist < 0) 357 | dist = -dist; 358 | a = p[0] - b; 359 | if (a < 0) 360 | a = -a; 361 | dist += a; 362 | if (dist < bestd) 363 | { 364 | a = p[2] - r; 365 | if (a < 0) 366 | a = -a; 367 | dist += a; 368 | if (dist < bestd) 369 | { 370 | bestd = dist; 371 | best = p[3]; 372 | } 373 | } 374 | } 375 | } 376 | } 377 | return (best); 378 | } 379 | public byte[] Process() 380 | { 381 | Learn(); 382 | Unbiasnet(); 383 | Inxbuild(); 384 | return ColorMap(); 385 | } 386 | 387 | /* Unbias network to give byte values 0..255 and record position i to prepare for sort 388 | ----------------------------------------------------------------------------------- */ 389 | public void Unbiasnet() 390 | { 391 | 392 | int i; 393 | 394 | for (i = 0; i < netsize; i++) 395 | { 396 | network[i][0] >>= netbiasshift; 397 | network[i][1] >>= netbiasshift; 398 | network[i][2] >>= netbiasshift; 399 | network[i][3] = i; /* record colour no */ 400 | } 401 | } 402 | 403 | /* Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in radpower[|i-j|] 404 | --------------------------------------------------------------------------------- */ 405 | protected void Alterneigh(int rad, int i, int b, int g, int r) 406 | { 407 | 408 | int j, k, lo, hi, a, m; 409 | int[] p; 410 | 411 | lo = i - rad; 412 | if (lo < -1) 413 | lo = -1; 414 | hi = i + rad; 415 | if (hi > netsize) 416 | hi = netsize; 417 | 418 | j = i + 1; 419 | k = i - 1; 420 | m = 1; 421 | while ((j < hi) || (k > lo)) 422 | { 423 | a = radpower[m++]; 424 | if (j < hi) 425 | { 426 | p = network[j++]; 427 | try 428 | { 429 | p[0] -= (a * (p[0] - b)) / alpharadbias; 430 | p[1] -= (a * (p[1] - g)) / alpharadbias; 431 | p[2] -= (a * (p[2] - r)) / alpharadbias; 432 | } 433 | catch (Exception) 434 | { 435 | } // prevents 1.3 miscompilation 436 | } 437 | if (k > lo) 438 | { 439 | p = network[k--]; 440 | try 441 | { 442 | p[0] -= (a * (p[0] - b)) / alpharadbias; 443 | p[1] -= (a * (p[1] - g)) / alpharadbias; 444 | p[2] -= (a * (p[2] - r)) / alpharadbias; 445 | } 446 | catch (Exception) 447 | { 448 | } 449 | } 450 | } 451 | } 452 | 453 | /* Move neuron i towards biased (b,g,r) by factor alpha 454 | ---------------------------------------------------- */ 455 | protected void Altersingle(int alpha, int i, int b, int g, int r) 456 | { 457 | 458 | /* alter hit neuron */ 459 | int[] n = network[i]; 460 | n[0] -= (alpha * (n[0] - b)) / initalpha; 461 | n[1] -= (alpha * (n[1] - g)) / initalpha; 462 | n[2] -= (alpha * (n[2] - r)) / initalpha; 463 | } 464 | 465 | /* Search for biased BGR values 466 | ---------------------------- */ 467 | protected int Contest(int b, int g, int r) 468 | { 469 | 470 | /* finds closest neuron (min dist) and updates freq */ 471 | /* finds best neuron (min dist-bias) and returns position */ 472 | /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */ 473 | /* bias[i] = gamma*((1/netsize)-freq[i]) */ 474 | 475 | int i, dist, a, biasdist, betafreq; 476 | int bestpos, bestbiaspos, bestd, bestbiasd; 477 | int[] n; 478 | 479 | bestd = ~(((int) 1) << 31); 480 | bestbiasd = bestd; 481 | bestpos = -1; 482 | bestbiaspos = bestpos; 483 | 484 | for (i = 0; i < netsize; i++) 485 | { 486 | n = network[i]; 487 | dist = n[0] - b; 488 | if (dist < 0) 489 | dist = -dist; 490 | a = n[1] - g; 491 | if (a < 0) 492 | a = -a; 493 | dist += a; 494 | a = n[2] - r; 495 | if (a < 0) 496 | a = -a; 497 | dist += a; 498 | if (dist < bestd) 499 | { 500 | bestd = dist; 501 | bestpos = i; 502 | } 503 | biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift)); 504 | if (biasdist < bestbiasd) 505 | { 506 | bestbiasd = biasdist; 507 | bestbiaspos = i; 508 | } 509 | betafreq = (freq[i] >> betashift); 510 | freq[i] -= betafreq; 511 | bias[i] += (betafreq << gammashift); 512 | } 513 | freq[bestpos] += beta; 514 | bias[bestpos] -= betagamma; 515 | return (bestbiaspos); 516 | } 517 | } 518 | } -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/IModuleManager/__cachecheck: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/IModuleManager/__cachecheck -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/IPersistenceManager/0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/IPersistenceManager/0 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/IPersistenceManager/__cachecheck: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/IPersistenceManager/__cachecheck -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/14: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/14 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/15: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/15 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/16: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/16 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/17: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/17 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/18: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/18 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/19: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/19 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/20: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/20 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/21: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/21 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/22: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/22 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/23: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/23 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/24: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/24 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/25: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/25 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/26: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/26 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/SymbolCache/__cachecheck: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/SymbolCache/__cachecheck -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/WordIndex/19: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/WordIndex/19 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/WordIndex/21: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/WordIndex/21 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/WordIndex/22: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/WordIndex/22 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/WordIndex/23: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/WordIndex/23 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/WordIndex/24: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/WordIndex/24 -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/WordIndex/__cachecheck: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/WordIndex/__cachecheck -------------------------------------------------------------------------------- /Components/_ReSharper.Gif/__cachecheck: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Components/_ReSharper.Gif/__cachecheck -------------------------------------------------------------------------------- /Example/App.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Example/App.ico -------------------------------------------------------------------------------- /Example/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | // 9 | [assembly: AssemblyTitle("")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("")] 14 | [assembly: AssemblyCopyright("")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | 18 | // 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Revision and Build Numbers 27 | // by using the '*' as shown below: 28 | 29 | [assembly: AssemblyVersion("1.0.*")] 30 | 31 | // 32 | // In order to sign your assembly you must specify a key to use. Refer to the 33 | // Microsoft .NET Framework documentation for more information on assembly signing. 34 | // 35 | // Use the attributes below to control which key is used for signing. 36 | // 37 | // Notes: 38 | // (*) If no key is specified, the assembly is not signed. 39 | // (*) KeyName refers to a key that has been installed in the Crypto Service 40 | // Provider (CSP) on your machine. KeyFile refers to a file which contains 41 | // a key. 42 | // (*) If the KeyFile and the KeyName values are both specified, the 43 | // following processing occurs: 44 | // (1) If the KeyName can be found in the CSP, that key is used. 45 | // (2) If the KeyName does not exist and the KeyFile does exist, the key 46 | // in the KeyFile is installed into the CSP and used. 47 | // (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. 48 | // When specifying the KeyFile, the location of the KeyFile should be 49 | // relative to the project output directory which is 50 | // %Project Directory%\obj\. For example, if your KeyFile is 51 | // located in the project directory, you would specify the AssemblyKeyFile 52 | // attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] 53 | // (*) Delay Signing is an advanced option - see the Microsoft .NET Framework 54 | // documentation for more information on this. 55 | // 56 | [assembly: AssemblyDelaySign(false)] 57 | [assembly: AssemblyKeyFile("")] 58 | [assembly: AssemblyKeyName("")] 59 | -------------------------------------------------------------------------------- /Example/Example.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Local 5 | 7.10.3077 6 | 2.0 7 | {1806268E-B13F-40DA-BC71-38E51BE921CE} 8 | Debug 9 | AnyCPU 10 | App.ico 11 | 12 | Example 13 | 14 | JScript 15 | Grid 16 | IE50 17 | false 18 | Exe 19 | Example 20 | OnBuildSuccess 21 | 22 | 23 | 24 | v2.0 25 | 26 | 27 | 0.0 28 | 29 | 30 | bin\Debug\ 31 | false 32 | 285212672 33 | false 34 | 35 | DEBUG;TRACE 36 | 37 | true 38 | 4096 39 | false 40 | 41 | false 42 | false 43 | false 44 | false 45 | 4 46 | full 47 | prompt 48 | 49 | 50 | bin\Release\ 51 | false 52 | 285212672 53 | false 54 | 55 | TRACE 56 | 57 | false 58 | 4096 59 | false 60 | 61 | true 62 | false 63 | false 64 | false 65 | 4 66 | none 67 | prompt 68 | 69 | 70 | 71 | Components 72 | {1C3A03CA-5E55-4CC4-9746-194A4188960A} 73 | {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 74 | 75 | 76 | System 77 | 78 | 79 | System.Data 80 | 81 | 82 | System.Drawing 83 | 84 | 85 | System.XML 86 | 87 | 88 | 89 | 90 | Code 91 | 92 | 93 | Code 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Example/ExampleMain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Drawing.Imaging; 4 | using System.IO; 5 | using Gif.Components; 6 | 7 | namespace Example 8 | { 9 | class ExampleMain 10 | { 11 | [STAThread] 12 | static void Main(string[] args) 13 | { 14 | /* create Gif */ 15 | //you should replace filepath 16 | String [] imageFilePaths = new String[]{"c:\\01.png","c:\\02.png","c:\\03.png"}; 17 | String outputFilePath = "c:\\test.gif"; 18 | AnimatedGifEncoder e = new AnimatedGifEncoder(); 19 | 20 | // read file as memorystream 21 | byte[] fileBytes = File.ReadAllBytes(outputFilePath); 22 | MemoryStream memStream = new MemoryStream(fileBytes); 23 | e.Start(memStream); 24 | e.SetDelay(500); 25 | //-1:no repeat,0:always repeat 26 | e.SetRepeat(0); 27 | for (int i = 0, count = imageFilePaths.Length; i < count; i++ ) 28 | { 29 | e.AddFrame( Image.FromFile( imageFilePaths[i] ) ); 30 | } 31 | e.Finish(); 32 | /* extract Gif */ 33 | string outputPath = "c:\\"; 34 | GifDecoder gifDecoder = new GifDecoder(); 35 | gifDecoder.Read( "c:\\test.gif" ); 36 | for ( int i = 0, count = gifDecoder.GetFrameCount(); i < count; i++ ) 37 | { 38 | Image frame = gifDecoder.GetFrame( i ); // frame i 39 | frame.Save( outputPath + Guid.NewGuid().ToString() + ".png", ImageFormat.Png ); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Example/Res/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Example/Res/01.png -------------------------------------------------------------------------------- /Example/Res/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Example/Res/02.png -------------------------------------------------------------------------------- /Example/Res/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Example/Res/03.png -------------------------------------------------------------------------------- /Example/Res/test.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Example/Res/test.gif -------------------------------------------------------------------------------- /NGif.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 14 3 | VisualStudioVersion = 14.0.24720.0 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Components", "Components\Components.csproj", "{1C3A03CA-5E55-4CC4-9746-194A4188960A}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{1806268E-B13F-40DA-BC71-38E51BE921CE}" 8 | EndProject 9 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3B6C5C83-263A-4AA5-BEF0-84779D899FFA}" 10 | ProjectSection(SolutionItems) = preProject 11 | Refs\gif.zip = Refs\gif.zip 12 | EndProjectSection 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {1C3A03CA-5E55-4CC4-9746-194A4188960A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {1C3A03CA-5E55-4CC4-9746-194A4188960A}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {1C3A03CA-5E55-4CC4-9746-194A4188960A}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {1C3A03CA-5E55-4CC4-9746-194A4188960A}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {1806268E-B13F-40DA-BC71-38E51BE921CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {1806268E-B13F-40DA-BC71-38E51BE921CE}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {1806268E-B13F-40DA-BC71-38E51BE921CE}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {1806268E-B13F-40DA-BC71-38E51BE921CE}.Release|Any CPU.Build.0 = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(SolutionProperties) = preSolution 30 | HideSolutionNode = FALSE 31 | EndGlobalSection 32 | EndGlobal 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nGif 2 | .NET advanced GIF library 3 | 4 | This repo is a fork of NGif-Animated-GIF-Encoder-for-NET by gOODiDEA.NET. He originally ported it from Java but it contained a few bugs and minor annoyances. This fork can animate GIFs frame by frame and supports writing to memory or file streams. 5 | 6 | The original implementation also had bugs with transparency appearing as black as well as the last two pixels rendering as the wrong color, but these have been fixed. 7 | 8 | See the original project here: 9 | http://www.codeproject.com/Articles/11505/NGif-Animated-GIF-Encoder-for-NET 10 | -------------------------------------------------------------------------------- /Refs/gif.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avianbc/NGif/40a4f5611b63fcb78f9560b95a35bd91a028158c/Refs/gif.zip --------------------------------------------------------------------------------