/// <summary> /// Analyzes image colors and creates color map. /// </summary> private void AnalyzePixels() { if (this.firstFrame) { // HEY YOU - THIS IS IMPORTANT! // // Unlike the code this project is built on, we generate a palette only once, using the data from the // first frame. This speeds up encoding (building the palette is slow) and makes it so that subsequent // frames use the same colors and look more consistent from frame to frame, but has an undesirable // side-effect of getting confused when radically different colors show up in later frames. This wasn't // a problem with Infinifactory, but if your game uses a deliberately reduced color palette you might // want to switch back to using per-frame palettes. // // This is, of course, left as an exercise for the reader. // initialize quantizer and create reduced palette this.nq = new NeuQuant(this.pixels, this.pixels.Length, this.sample); this.colorTab = nq.Process(); // create the buffer for the displayed pixel data: this.visiblePixels = new byte[this.pixels.Length]; } int nPix = this.pixels.Length / 3; this.indexedPixels = new byte[nPix]; // map image pixels to new palette for (int i = 0; i < nPix; i++) { int r = i * 3 + 0; int g = r + 1; int b = g + 1; // HEY YOU - THIS IS IMPORTANT! // // As you may know, animated GIFs aren't exactly the most efficient way to encode video; notably, // they lack the interframe compression found in real video formats. However, we can do something // similar by taking advantage of the GIF format's ability to "build on" a previous frame by // specifying pixels as transparent and allowing the previous frame's image to show through in those // locations. If a pixel doesn't change much from a previous pixel it will be replaced with the // transparent color, and if enough of those pixels are used in a frame it will compress much better // than if you had all the original color data for the frame. // // In a game like Infinifactory, where the recording camera is locked and only a small portion of // the scene is animated, this reduced the size of GIFs to about 1/3 of their original size. const int ChangeDelta = 3; bool pixelRequired = this.firstFrame || Math.Abs(this.pixels[r] - this.visiblePixels[r]) > ChangeDelta || Math.Abs(this.pixels[g] - this.visiblePixels[g]) > ChangeDelta || Math.Abs(this.pixels[b] - this.visiblePixels[b]) > ChangeDelta; int index; if (pixelRequired) { this.visiblePixels[r] = this.pixels[r]; this.visiblePixels[g] = this.pixels[g]; this.visiblePixels[b] = this.pixels[b]; index = this.nq.Map(this.pixels[r], this.pixels[g], this.pixels[b]); } else { index = NeuQuant.PaletteSize; } this.usedEntry[index] = true; this.indexedPixels[i] = (byte)index; } this.colorDepth = (int)Math.Log(NeuQuant.PaletteSize + 1, 2); this.palSize = this.colorDepth - 1; }
/** * Analyzes image colors and creates color map. */ protected void AnalyzePixels() { int len = pixels.Length; int nPix = len / 3; indexedPixels = new byte[nPix]; NeuQuant nq = new NeuQuant(pixels, len, sample); // initialize quantizer colorTab = nq.Process(); // create reduced palette // convert map from BGR to RGB // for (int i = 0; i < colorTab.Length; i += 3) // { // byte temp = colorTab[i]; // colorTab[i] = colorTab[i + 2]; // colorTab[i + 2] = temp; // usedEntry[i / 3] = false; // } // map image pixels to new palette int k = 0; for (int i = 0; i < nPix; i++) { int index = nq.Map(pixels[k++] & 0xff, pixels[k++] & 0xff, pixels[k++] & 0xff); usedEntry[index] = true; indexedPixels[i] = (byte) index; } pixels = null; colorDepth = 8; palSize = 7; // get closest match to transparent color if specified if (transparent != Color.Empty ) { transIndex = FindClosest(transparent); } }