13a5750359cb0362d01d394032944854fdde4069eSam Juddpackage com.bumptech.glide.gifencoder; 23a5750359cb0362d01d394032944854fdde4069eSam Judd 3f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd 4f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Juddimport android.graphics.Bitmap; 5f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Juddimport android.graphics.Canvas; 63a5750359cb0362d01d394032944854fdde4069eSam Juddimport android.graphics.Color; 7284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Juddimport android.util.Log; 83a5750359cb0362d01d394032944854fdde4069eSam Judd 93a5750359cb0362d01d394032944854fdde4069eSam Juddimport java.io.BufferedOutputStream; 103a5750359cb0362d01d394032944854fdde4069eSam Juddimport java.io.FileOutputStream; 113a5750359cb0362d01d394032944854fdde4069eSam Juddimport java.io.IOException; 123a5750359cb0362d01d394032944854fdde4069eSam Juddimport java.io.OutputStream; 133a5750359cb0362d01d394032944854fdde4069eSam Judd 143a5750359cb0362d01d394032944854fdde4069eSam Judd/** 153a5750359cb0362d01d394032944854fdde4069eSam Judd * Class AnimatedGifEncoder - Encodes a GIF file consisting of one or more 163a5750359cb0362d01d394032944854fdde4069eSam Judd * frames. 173a5750359cb0362d01d394032944854fdde4069eSam Judd * 183a5750359cb0362d01d394032944854fdde4069eSam Judd * <pre> 193a5750359cb0362d01d394032944854fdde4069eSam Judd * Example: 203a5750359cb0362d01d394032944854fdde4069eSam Judd * AnimatedGifEncoder e = new AnimatedGifEncoder(); 213a5750359cb0362d01d394032944854fdde4069eSam Judd * e.start(outputFileName); 223a5750359cb0362d01d394032944854fdde4069eSam Judd * e.setDelay(1000); // 1 frame per sec 233a5750359cb0362d01d394032944854fdde4069eSam Judd * e.addFrame(image1); 243a5750359cb0362d01d394032944854fdde4069eSam Judd * e.addFrame(image2); 253a5750359cb0362d01d394032944854fdde4069eSam Judd * e.finish(); 263a5750359cb0362d01d394032944854fdde4069eSam Judd * </pre> 273a5750359cb0362d01d394032944854fdde4069eSam Judd * 283a5750359cb0362d01d394032944854fdde4069eSam Judd * No copyright asserted on the source code of this class. May be used for any 293a5750359cb0362d01d394032944854fdde4069eSam Judd * purpose, however, refer to the Unisys LZW patent for restrictions on use of 303a5750359cb0362d01d394032944854fdde4069eSam Judd * the associated LZWEncoder class. Please forward any corrections to 313a5750359cb0362d01d394032944854fdde4069eSam Judd * kweiner@fmsware.com. 323a5750359cb0362d01d394032944854fdde4069eSam Judd * 333a5750359cb0362d01d394032944854fdde4069eSam Judd * @author Kevin Weiner, FM Software 343a5750359cb0362d01d394032944854fdde4069eSam Judd * @version 1.03 November 2003 353a5750359cb0362d01d394032944854fdde4069eSam Judd * 363a5750359cb0362d01d394032944854fdde4069eSam Judd */ 373a5750359cb0362d01d394032944854fdde4069eSam Judd 383a5750359cb0362d01d394032944854fdde4069eSam Juddpublic class AnimatedGifEncoder { 39284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd private static final String TAG = "AnimatedGifEncoder"; 40284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd 41284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd // The minimum % of an images pixels that must be transparent for us to set a transparent index automatically. 42284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd private static final double MIN_TRANSPARENT_PERCENTAGE = 4d; 433a5750359cb0362d01d394032944854fdde4069eSam Judd 442c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private int width; // image size 453a5750359cb0362d01d394032944854fdde4069eSam Judd 462c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private int height; 473a5750359cb0362d01d394032944854fdde4069eSam Judd 482c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private Integer transparent = null; // transparent color if given 493a5750359cb0362d01d394032944854fdde4069eSam Judd 502c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private int transIndex; // transparent index in color table 513a5750359cb0362d01d394032944854fdde4069eSam Judd 522c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private int repeat = -1; // no repeat 533a5750359cb0362d01d394032944854fdde4069eSam Judd 542c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private int delay = 0; // frame delay (hundredths) 553a5750359cb0362d01d394032944854fdde4069eSam Judd 562c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private boolean started = false; // ready to output frames 573a5750359cb0362d01d394032944854fdde4069eSam Judd 582c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private OutputStream out; 593a5750359cb0362d01d394032944854fdde4069eSam Judd 602c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private Bitmap image; // current frame 613a5750359cb0362d01d394032944854fdde4069eSam Judd 622c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private byte[] pixels; // BGR byte array from frame 633a5750359cb0362d01d394032944854fdde4069eSam Judd 642c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private byte[] indexedPixels; // converted frame indexed to palette 653a5750359cb0362d01d394032944854fdde4069eSam Judd 662c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private int colorDepth; // number of bit planes 673a5750359cb0362d01d394032944854fdde4069eSam Judd 682c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private byte[] colorTab; // RGB palette 693a5750359cb0362d01d394032944854fdde4069eSam Judd 702c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private boolean[] usedEntry = new boolean[256]; // active palette entries 713a5750359cb0362d01d394032944854fdde4069eSam Judd 722c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private int palSize = 7; // color table size (bits-1) 733a5750359cb0362d01d394032944854fdde4069eSam Judd 742c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private int dispose = -1; // disposal code (-1 = use default) 753a5750359cb0362d01d394032944854fdde4069eSam Judd 762c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private boolean closeStream = false; // close stream when finished 773a5750359cb0362d01d394032944854fdde4069eSam Judd 782c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private boolean firstFrame = true; 793a5750359cb0362d01d394032944854fdde4069eSam Judd 802c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private boolean sizeSet = false; // if false, get size from first frame 813a5750359cb0362d01d394032944854fdde4069eSam Judd 822c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private int sample = 10; // default sample interval for quantizer 832c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd 842c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private boolean hasTransparentPixels; 853a5750359cb0362d01d394032944854fdde4069eSam Judd 863a5750359cb0362d01d394032944854fdde4069eSam Judd /** 873a5750359cb0362d01d394032944854fdde4069eSam Judd * Sets the delay time between each frame, or changes it for subsequent frames 883a5750359cb0362d01d394032944854fdde4069eSam Judd * (applies to last frame added). 893a5750359cb0362d01d394032944854fdde4069eSam Judd * 903a5750359cb0362d01d394032944854fdde4069eSam Judd * @param ms 913a5750359cb0362d01d394032944854fdde4069eSam Judd * int delay time in milliseconds 923a5750359cb0362d01d394032944854fdde4069eSam Judd */ 933a5750359cb0362d01d394032944854fdde4069eSam Judd public void setDelay(int ms) { 943a5750359cb0362d01d394032944854fdde4069eSam Judd delay = Math.round(ms / 10.0f); 953a5750359cb0362d01d394032944854fdde4069eSam Judd } 963a5750359cb0362d01d394032944854fdde4069eSam Judd 973a5750359cb0362d01d394032944854fdde4069eSam Judd /** 983a5750359cb0362d01d394032944854fdde4069eSam Judd * Sets the GIF frame disposal code for the last added frame and any 993a5750359cb0362d01d394032944854fdde4069eSam Judd * subsequent frames. Default is 0 if no transparent color has been set, 1003a5750359cb0362d01d394032944854fdde4069eSam Judd * otherwise 2. 1013a5750359cb0362d01d394032944854fdde4069eSam Judd * 1023a5750359cb0362d01d394032944854fdde4069eSam Judd * @param code 1033a5750359cb0362d01d394032944854fdde4069eSam Judd * int disposal code. 1043a5750359cb0362d01d394032944854fdde4069eSam Judd */ 1053a5750359cb0362d01d394032944854fdde4069eSam Judd public void setDispose(int code) { 1063a5750359cb0362d01d394032944854fdde4069eSam Judd if (code >= 0) { 1073a5750359cb0362d01d394032944854fdde4069eSam Judd dispose = code; 1083a5750359cb0362d01d394032944854fdde4069eSam Judd } 1093a5750359cb0362d01d394032944854fdde4069eSam Judd } 1103a5750359cb0362d01d394032944854fdde4069eSam Judd 1113a5750359cb0362d01d394032944854fdde4069eSam Judd /** 1123a5750359cb0362d01d394032944854fdde4069eSam Judd * Sets the number of times the set of GIF frames should be played. Default is 1133a5750359cb0362d01d394032944854fdde4069eSam Judd * 1; 0 means play indefinitely. Must be invoked before the first image is 1143a5750359cb0362d01d394032944854fdde4069eSam Judd * added. 1153a5750359cb0362d01d394032944854fdde4069eSam Judd * 1163a5750359cb0362d01d394032944854fdde4069eSam Judd * @param iter 1173a5750359cb0362d01d394032944854fdde4069eSam Judd * int number of iterations. 1183a5750359cb0362d01d394032944854fdde4069eSam Judd */ 1193a5750359cb0362d01d394032944854fdde4069eSam Judd public void setRepeat(int iter) { 1203a5750359cb0362d01d394032944854fdde4069eSam Judd if (iter >= 0) { 1213a5750359cb0362d01d394032944854fdde4069eSam Judd repeat = iter; 1223a5750359cb0362d01d394032944854fdde4069eSam Judd } 1233a5750359cb0362d01d394032944854fdde4069eSam Judd } 1243a5750359cb0362d01d394032944854fdde4069eSam Judd 1253a5750359cb0362d01d394032944854fdde4069eSam Judd /** 1263a5750359cb0362d01d394032944854fdde4069eSam Judd * Sets the transparent color for the last added frame and any subsequent 1273a5750359cb0362d01d394032944854fdde4069eSam Judd * frames. Since all colors are subject to modification in the quantization 1283a5750359cb0362d01d394032944854fdde4069eSam Judd * process, the color in the final palette for each frame closest to the given 1293a5750359cb0362d01d394032944854fdde4069eSam Judd * color becomes the transparent color for that frame. May be set to null to 1303a5750359cb0362d01d394032944854fdde4069eSam Judd * indicate no transparent color. 1313a5750359cb0362d01d394032944854fdde4069eSam Judd * 132f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd * @param color 1333a5750359cb0362d01d394032944854fdde4069eSam Judd * Color to be treated as transparent on display. 1343a5750359cb0362d01d394032944854fdde4069eSam Judd */ 135f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd public void setTransparent(int color) { 136f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd transparent = color; 1373a5750359cb0362d01d394032944854fdde4069eSam Judd } 1383a5750359cb0362d01d394032944854fdde4069eSam Judd 1393a5750359cb0362d01d394032944854fdde4069eSam Judd /** 1403a5750359cb0362d01d394032944854fdde4069eSam Judd * Adds next GIF frame. The frame is not written immediately, but is actually 1413a5750359cb0362d01d394032944854fdde4069eSam Judd * deferred until the next frame is received so that timing data can be 1423a5750359cb0362d01d394032944854fdde4069eSam Judd * inserted. Invoking <code>finish()</code> flushes all frames. If 1433a5750359cb0362d01d394032944854fdde4069eSam Judd * <code>setSize</code> was not invoked, the size of the first image is used 1443a5750359cb0362d01d394032944854fdde4069eSam Judd * for all subsequent frames. 1453a5750359cb0362d01d394032944854fdde4069eSam Judd * 1463a5750359cb0362d01d394032944854fdde4069eSam Judd * @param im 1473a5750359cb0362d01d394032944854fdde4069eSam Judd * BufferedImage containing frame to write. 1483a5750359cb0362d01d394032944854fdde4069eSam Judd * @return true if successful. 1493a5750359cb0362d01d394032944854fdde4069eSam Judd */ 150f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd public boolean addFrame(Bitmap im) { 1513a5750359cb0362d01d394032944854fdde4069eSam Judd if ((im == null) || !started) { 1523a5750359cb0362d01d394032944854fdde4069eSam Judd return false; 1533a5750359cb0362d01d394032944854fdde4069eSam Judd } 1543a5750359cb0362d01d394032944854fdde4069eSam Judd boolean ok = true; 1553a5750359cb0362d01d394032944854fdde4069eSam Judd try { 1563a5750359cb0362d01d394032944854fdde4069eSam Judd if (!sizeSet) { 1573a5750359cb0362d01d394032944854fdde4069eSam Judd // use first frame's size 1583a5750359cb0362d01d394032944854fdde4069eSam Judd setSize(im.getWidth(), im.getHeight()); 1593a5750359cb0362d01d394032944854fdde4069eSam Judd } 1603a5750359cb0362d01d394032944854fdde4069eSam Judd image = im; 1613a5750359cb0362d01d394032944854fdde4069eSam Judd getImagePixels(); // convert to correct format if necessary 1623a5750359cb0362d01d394032944854fdde4069eSam Judd analyzePixels(); // build color table & map pixels 1633a5750359cb0362d01d394032944854fdde4069eSam Judd if (firstFrame) { 1643a5750359cb0362d01d394032944854fdde4069eSam Judd writeLSD(); // logical screen descriptior 1653a5750359cb0362d01d394032944854fdde4069eSam Judd writePalette(); // global color table 1663a5750359cb0362d01d394032944854fdde4069eSam Judd if (repeat >= 0) { 1673a5750359cb0362d01d394032944854fdde4069eSam Judd // use NS app extension to indicate reps 1683a5750359cb0362d01d394032944854fdde4069eSam Judd writeNetscapeExt(); 1693a5750359cb0362d01d394032944854fdde4069eSam Judd } 1703a5750359cb0362d01d394032944854fdde4069eSam Judd } 1713a5750359cb0362d01d394032944854fdde4069eSam Judd writeGraphicCtrlExt(); // write graphic control extension 1723a5750359cb0362d01d394032944854fdde4069eSam Judd writeImageDesc(); // image descriptor 1733a5750359cb0362d01d394032944854fdde4069eSam Judd if (!firstFrame) { 1743a5750359cb0362d01d394032944854fdde4069eSam Judd writePalette(); // local color table 1753a5750359cb0362d01d394032944854fdde4069eSam Judd } 1763a5750359cb0362d01d394032944854fdde4069eSam Judd writePixels(); // encode and write pixel data 1773a5750359cb0362d01d394032944854fdde4069eSam Judd firstFrame = false; 1783a5750359cb0362d01d394032944854fdde4069eSam Judd } catch (IOException e) { 1793a5750359cb0362d01d394032944854fdde4069eSam Judd ok = false; 1803a5750359cb0362d01d394032944854fdde4069eSam Judd } 1813a5750359cb0362d01d394032944854fdde4069eSam Judd 1823a5750359cb0362d01d394032944854fdde4069eSam Judd return ok; 1833a5750359cb0362d01d394032944854fdde4069eSam Judd } 1843a5750359cb0362d01d394032944854fdde4069eSam Judd 1853a5750359cb0362d01d394032944854fdde4069eSam Judd /** 1863a5750359cb0362d01d394032944854fdde4069eSam Judd * Flushes any pending data and closes output file. If writing to an 1873a5750359cb0362d01d394032944854fdde4069eSam Judd * OutputStream, the stream is not closed. 1883a5750359cb0362d01d394032944854fdde4069eSam Judd */ 1893a5750359cb0362d01d394032944854fdde4069eSam Judd public boolean finish() { 1903a5750359cb0362d01d394032944854fdde4069eSam Judd if (!started) 1913a5750359cb0362d01d394032944854fdde4069eSam Judd return false; 1923a5750359cb0362d01d394032944854fdde4069eSam Judd boolean ok = true; 1933a5750359cb0362d01d394032944854fdde4069eSam Judd started = false; 1943a5750359cb0362d01d394032944854fdde4069eSam Judd try { 1953a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0x3b); // gif trailer 1963a5750359cb0362d01d394032944854fdde4069eSam Judd out.flush(); 1973a5750359cb0362d01d394032944854fdde4069eSam Judd if (closeStream) { 1983a5750359cb0362d01d394032944854fdde4069eSam Judd out.close(); 1993a5750359cb0362d01d394032944854fdde4069eSam Judd } 2003a5750359cb0362d01d394032944854fdde4069eSam Judd } catch (IOException e) { 2013a5750359cb0362d01d394032944854fdde4069eSam Judd ok = false; 2023a5750359cb0362d01d394032944854fdde4069eSam Judd } 2033a5750359cb0362d01d394032944854fdde4069eSam Judd 2043a5750359cb0362d01d394032944854fdde4069eSam Judd // reset for subsequent use 2053a5750359cb0362d01d394032944854fdde4069eSam Judd transIndex = 0; 2063a5750359cb0362d01d394032944854fdde4069eSam Judd out = null; 2073a5750359cb0362d01d394032944854fdde4069eSam Judd image = null; 2083a5750359cb0362d01d394032944854fdde4069eSam Judd pixels = null; 2093a5750359cb0362d01d394032944854fdde4069eSam Judd indexedPixels = null; 2103a5750359cb0362d01d394032944854fdde4069eSam Judd colorTab = null; 2113a5750359cb0362d01d394032944854fdde4069eSam Judd closeStream = false; 2123a5750359cb0362d01d394032944854fdde4069eSam Judd firstFrame = true; 2133a5750359cb0362d01d394032944854fdde4069eSam Judd 2143a5750359cb0362d01d394032944854fdde4069eSam Judd return ok; 2153a5750359cb0362d01d394032944854fdde4069eSam Judd } 2163a5750359cb0362d01d394032944854fdde4069eSam Judd 2173a5750359cb0362d01d394032944854fdde4069eSam Judd /** 2183a5750359cb0362d01d394032944854fdde4069eSam Judd * Sets frame rate in frames per second. Equivalent to 2193a5750359cb0362d01d394032944854fdde4069eSam Judd * <code>setDelay(1000/fps)</code>. 2203a5750359cb0362d01d394032944854fdde4069eSam Judd * 2213a5750359cb0362d01d394032944854fdde4069eSam Judd * @param fps 2223a5750359cb0362d01d394032944854fdde4069eSam Judd * float frame rate (frames per second) 2233a5750359cb0362d01d394032944854fdde4069eSam Judd */ 2243a5750359cb0362d01d394032944854fdde4069eSam Judd public void setFrameRate(float fps) { 2253a5750359cb0362d01d394032944854fdde4069eSam Judd if (fps != 0f) { 2263a5750359cb0362d01d394032944854fdde4069eSam Judd delay = Math.round(100f / fps); 2273a5750359cb0362d01d394032944854fdde4069eSam Judd } 2283a5750359cb0362d01d394032944854fdde4069eSam Judd } 2293a5750359cb0362d01d394032944854fdde4069eSam Judd 2303a5750359cb0362d01d394032944854fdde4069eSam Judd /** 2313a5750359cb0362d01d394032944854fdde4069eSam Judd * Sets quality of color quantization (conversion of images to the maximum 256 2323a5750359cb0362d01d394032944854fdde4069eSam Judd * colors allowed by the GIF specification). Lower values (minimum = 1) 2333a5750359cb0362d01d394032944854fdde4069eSam Judd * produce better colors, but slow processing significantly. 10 is the 2343a5750359cb0362d01d394032944854fdde4069eSam Judd * default, and produces good color mapping at reasonable speeds. Values 2353a5750359cb0362d01d394032944854fdde4069eSam Judd * greater than 20 do not yield significant improvements in speed. 2363a5750359cb0362d01d394032944854fdde4069eSam Judd * 2371737514a35eae271f1a7d5441b92f9ea9c048e27Sam Judd * @param quality int greater than 0. 2383a5750359cb0362d01d394032944854fdde4069eSam Judd */ 2393a5750359cb0362d01d394032944854fdde4069eSam Judd public void setQuality(int quality) { 2403a5750359cb0362d01d394032944854fdde4069eSam Judd if (quality < 1) 2413a5750359cb0362d01d394032944854fdde4069eSam Judd quality = 1; 2423a5750359cb0362d01d394032944854fdde4069eSam Judd sample = quality; 2433a5750359cb0362d01d394032944854fdde4069eSam Judd } 2443a5750359cb0362d01d394032944854fdde4069eSam Judd 2453a5750359cb0362d01d394032944854fdde4069eSam Judd /** 2463a5750359cb0362d01d394032944854fdde4069eSam Judd * Sets the GIF frame size. The default size is the size of the first frame 2473a5750359cb0362d01d394032944854fdde4069eSam Judd * added if this method is not invoked. 2483a5750359cb0362d01d394032944854fdde4069eSam Judd * 2493a5750359cb0362d01d394032944854fdde4069eSam Judd * @param w 2503a5750359cb0362d01d394032944854fdde4069eSam Judd * int frame width. 2513a5750359cb0362d01d394032944854fdde4069eSam Judd * @param h 2523a5750359cb0362d01d394032944854fdde4069eSam Judd * int frame width. 2533a5750359cb0362d01d394032944854fdde4069eSam Judd */ 2543a5750359cb0362d01d394032944854fdde4069eSam Judd public void setSize(int w, int h) { 2553a5750359cb0362d01d394032944854fdde4069eSam Judd if (started && !firstFrame) 2563a5750359cb0362d01d394032944854fdde4069eSam Judd return; 2573a5750359cb0362d01d394032944854fdde4069eSam Judd width = w; 2583a5750359cb0362d01d394032944854fdde4069eSam Judd height = h; 2593a5750359cb0362d01d394032944854fdde4069eSam Judd if (width < 1) 2603a5750359cb0362d01d394032944854fdde4069eSam Judd width = 320; 2613a5750359cb0362d01d394032944854fdde4069eSam Judd if (height < 1) 2623a5750359cb0362d01d394032944854fdde4069eSam Judd height = 240; 2633a5750359cb0362d01d394032944854fdde4069eSam Judd sizeSet = true; 2643a5750359cb0362d01d394032944854fdde4069eSam Judd } 2653a5750359cb0362d01d394032944854fdde4069eSam Judd 2663a5750359cb0362d01d394032944854fdde4069eSam Judd /** 2673a5750359cb0362d01d394032944854fdde4069eSam Judd * Initiates GIF file creation on the given stream. The stream is not closed 2683a5750359cb0362d01d394032944854fdde4069eSam Judd * automatically. 2693a5750359cb0362d01d394032944854fdde4069eSam Judd * 2703a5750359cb0362d01d394032944854fdde4069eSam Judd * @param os 2713a5750359cb0362d01d394032944854fdde4069eSam Judd * OutputStream on which GIF images are written. 2723a5750359cb0362d01d394032944854fdde4069eSam Judd * @return false if initial write failed. 2733a5750359cb0362d01d394032944854fdde4069eSam Judd */ 2743a5750359cb0362d01d394032944854fdde4069eSam Judd public boolean start(OutputStream os) { 2753a5750359cb0362d01d394032944854fdde4069eSam Judd if (os == null) 2763a5750359cb0362d01d394032944854fdde4069eSam Judd return false; 2773a5750359cb0362d01d394032944854fdde4069eSam Judd boolean ok = true; 2783a5750359cb0362d01d394032944854fdde4069eSam Judd closeStream = false; 2793a5750359cb0362d01d394032944854fdde4069eSam Judd out = os; 2803a5750359cb0362d01d394032944854fdde4069eSam Judd try { 2813a5750359cb0362d01d394032944854fdde4069eSam Judd writeString("GIF89a"); // header 2823a5750359cb0362d01d394032944854fdde4069eSam Judd } catch (IOException e) { 2833a5750359cb0362d01d394032944854fdde4069eSam Judd ok = false; 2843a5750359cb0362d01d394032944854fdde4069eSam Judd } 2853a5750359cb0362d01d394032944854fdde4069eSam Judd return started = ok; 2863a5750359cb0362d01d394032944854fdde4069eSam Judd } 2873a5750359cb0362d01d394032944854fdde4069eSam Judd 2883a5750359cb0362d01d394032944854fdde4069eSam Judd /** 2893a5750359cb0362d01d394032944854fdde4069eSam Judd * Initiates writing of a GIF file with the specified name. 2903a5750359cb0362d01d394032944854fdde4069eSam Judd * 2913a5750359cb0362d01d394032944854fdde4069eSam Judd * @param file 2923a5750359cb0362d01d394032944854fdde4069eSam Judd * String containing output file name. 2933a5750359cb0362d01d394032944854fdde4069eSam Judd * @return false if open or initial write failed. 2943a5750359cb0362d01d394032944854fdde4069eSam Judd */ 2953a5750359cb0362d01d394032944854fdde4069eSam Judd public boolean start(String file) { 2963a5750359cb0362d01d394032944854fdde4069eSam Judd boolean ok = true; 2973a5750359cb0362d01d394032944854fdde4069eSam Judd try { 2983a5750359cb0362d01d394032944854fdde4069eSam Judd out = new BufferedOutputStream(new FileOutputStream(file)); 2993a5750359cb0362d01d394032944854fdde4069eSam Judd ok = start(out); 3003a5750359cb0362d01d394032944854fdde4069eSam Judd closeStream = true; 3013a5750359cb0362d01d394032944854fdde4069eSam Judd } catch (IOException e) { 3023a5750359cb0362d01d394032944854fdde4069eSam Judd ok = false; 3033a5750359cb0362d01d394032944854fdde4069eSam Judd } 3043a5750359cb0362d01d394032944854fdde4069eSam Judd return started = ok; 3053a5750359cb0362d01d394032944854fdde4069eSam Judd } 3063a5750359cb0362d01d394032944854fdde4069eSam Judd 3073a5750359cb0362d01d394032944854fdde4069eSam Judd /** 3083a5750359cb0362d01d394032944854fdde4069eSam Judd * Analyzes image colors and creates color map. 3093a5750359cb0362d01d394032944854fdde4069eSam Judd */ 3102c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private void analyzePixels() { 3113a5750359cb0362d01d394032944854fdde4069eSam Judd int len = pixels.length; 3123a5750359cb0362d01d394032944854fdde4069eSam Judd int nPix = len / 3; 3133a5750359cb0362d01d394032944854fdde4069eSam Judd indexedPixels = new byte[nPix]; 3143a5750359cb0362d01d394032944854fdde4069eSam Judd NeuQuant nq = new NeuQuant(pixels, len, sample); 3153a5750359cb0362d01d394032944854fdde4069eSam Judd // initialize quantizer 3163a5750359cb0362d01d394032944854fdde4069eSam Judd colorTab = nq.process(); // create reduced palette 3173a5750359cb0362d01d394032944854fdde4069eSam Judd // convert map from BGR to RGB 3183a5750359cb0362d01d394032944854fdde4069eSam Judd for (int i = 0; i < colorTab.length; i += 3) { 3193a5750359cb0362d01d394032944854fdde4069eSam Judd byte temp = colorTab[i]; 3203a5750359cb0362d01d394032944854fdde4069eSam Judd colorTab[i] = colorTab[i + 2]; 3213a5750359cb0362d01d394032944854fdde4069eSam Judd colorTab[i + 2] = temp; 3223a5750359cb0362d01d394032944854fdde4069eSam Judd usedEntry[i / 3] = false; 3233a5750359cb0362d01d394032944854fdde4069eSam Judd } 3243a5750359cb0362d01d394032944854fdde4069eSam Judd // map image pixels to new palette 3253a5750359cb0362d01d394032944854fdde4069eSam Judd int k = 0; 3263a5750359cb0362d01d394032944854fdde4069eSam Judd for (int i = 0; i < nPix; i++) { 3273a5750359cb0362d01d394032944854fdde4069eSam Judd int index = nq.map(pixels[k++] & 0xff, pixels[k++] & 0xff, pixels[k++] & 0xff); 3283a5750359cb0362d01d394032944854fdde4069eSam Judd usedEntry[index] = true; 3293a5750359cb0362d01d394032944854fdde4069eSam Judd indexedPixels[i] = (byte) index; 3303a5750359cb0362d01d394032944854fdde4069eSam Judd } 3313a5750359cb0362d01d394032944854fdde4069eSam Judd pixels = null; 3323a5750359cb0362d01d394032944854fdde4069eSam Judd colorDepth = 8; 3333a5750359cb0362d01d394032944854fdde4069eSam Judd palSize = 7; 3343a5750359cb0362d01d394032944854fdde4069eSam Judd // get closest match to transparent color if specified 3353a5750359cb0362d01d394032944854fdde4069eSam Judd if (transparent != null) { 3363a5750359cb0362d01d394032944854fdde4069eSam Judd transIndex = findClosest(transparent); 3372c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd } else if (hasTransparentPixels) { 3382c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd transIndex = findClosest(Color.TRANSPARENT); 3393a5750359cb0362d01d394032944854fdde4069eSam Judd } 3403a5750359cb0362d01d394032944854fdde4069eSam Judd } 3413a5750359cb0362d01d394032944854fdde4069eSam Judd 3423a5750359cb0362d01d394032944854fdde4069eSam Judd /** 3433a5750359cb0362d01d394032944854fdde4069eSam Judd * Returns index of palette color closest to c 3443a5750359cb0362d01d394032944854fdde4069eSam Judd * 3453a5750359cb0362d01d394032944854fdde4069eSam Judd */ 3462c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private int findClosest(int color) { 3473a5750359cb0362d01d394032944854fdde4069eSam Judd if (colorTab == null) 3483a5750359cb0362d01d394032944854fdde4069eSam Judd return -1; 349f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd int r = Color.red(color); 350f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd int g = Color.green(color); 351f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd int b = Color.blue(color); 3523a5750359cb0362d01d394032944854fdde4069eSam Judd int minpos = 0; 3533a5750359cb0362d01d394032944854fdde4069eSam Judd int dmin = 256 * 256 * 256; 3543a5750359cb0362d01d394032944854fdde4069eSam Judd int len = colorTab.length; 3553a5750359cb0362d01d394032944854fdde4069eSam Judd for (int i = 0; i < len;) { 3563a5750359cb0362d01d394032944854fdde4069eSam Judd int dr = r - (colorTab[i++] & 0xff); 3573a5750359cb0362d01d394032944854fdde4069eSam Judd int dg = g - (colorTab[i++] & 0xff); 3583a5750359cb0362d01d394032944854fdde4069eSam Judd int db = b - (colorTab[i] & 0xff); 3593a5750359cb0362d01d394032944854fdde4069eSam Judd int d = dr * dr + dg * dg + db * db; 3603a5750359cb0362d01d394032944854fdde4069eSam Judd int index = i / 3; 3613a5750359cb0362d01d394032944854fdde4069eSam Judd if (usedEntry[index] && (d < dmin)) { 3623a5750359cb0362d01d394032944854fdde4069eSam Judd dmin = d; 3633a5750359cb0362d01d394032944854fdde4069eSam Judd minpos = index; 3643a5750359cb0362d01d394032944854fdde4069eSam Judd } 3653a5750359cb0362d01d394032944854fdde4069eSam Judd i++; 3663a5750359cb0362d01d394032944854fdde4069eSam Judd } 3673a5750359cb0362d01d394032944854fdde4069eSam Judd return minpos; 3683a5750359cb0362d01d394032944854fdde4069eSam Judd } 3693a5750359cb0362d01d394032944854fdde4069eSam Judd 3703a5750359cb0362d01d394032944854fdde4069eSam Judd /** 3713a5750359cb0362d01d394032944854fdde4069eSam Judd * Extracts image pixels into byte array "pixels" 3723a5750359cb0362d01d394032944854fdde4069eSam Judd */ 3732c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private void getImagePixels() { 3743a5750359cb0362d01d394032944854fdde4069eSam Judd int w = image.getWidth(); 3753a5750359cb0362d01d394032944854fdde4069eSam Judd int h = image.getHeight(); 376f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd 377f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd if ((w != width) || (h != height)) { 3783a5750359cb0362d01d394032944854fdde4069eSam Judd // create new image with right size/format 379f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd Bitmap temp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 380f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd Canvas canvas = new Canvas(temp); 381f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd canvas.drawBitmap(temp, 0, 0, null); 3823a5750359cb0362d01d394032944854fdde4069eSam Judd image = temp; 3833a5750359cb0362d01d394032944854fdde4069eSam Judd } 384f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd int[] pixelsInt = new int[w * h]; 385f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd image.getPixels(pixelsInt, 0, w, 0, 0, w, h); 386f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd 387f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd // The algorithm requires 3 bytes per pixel as RGB. 388f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd pixels = new byte[pixelsInt.length * 3]; 389f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd 390f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd int pixelsIndex = 0; 3912c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd hasTransparentPixels = false; 392284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd int totalTransparentPixels = 0; 393f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd for (final int pixel : pixelsInt) { 3942c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd if (pixel == Color.TRANSPARENT) { 395284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd totalTransparentPixels++; 3962c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd } 397f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd pixels[pixelsIndex++] = (byte) (pixel & 0xFF); 398f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd pixels[pixelsIndex++] = (byte) ((pixel >> 8) & 0xFF); 399f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd pixels[pixelsIndex++] = (byte) ((pixel >> 16) & 0xFF); 400f7b3e5d7a4893fd55b3fd36be56bb37319d8aa24Sam Judd } 401284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd 402284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd double transparentPercentage = 100 * totalTransparentPixels / (double) pixelsInt.length; 403284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd // Assume images with greater where more than n% of the pixels are transparent actually have transparency. 404284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd // See issue #214. 405284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd hasTransparentPixels = transparentPercentage > MIN_TRANSPARENT_PERCENTAGE; 406284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd if (Log.isLoggable(TAG, Log.DEBUG)) { 407284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd Log.d(TAG, "got pixels for frame with " + transparentPercentage + "% transparent pixels"); 408284bbcd0ed2c8f747a3b6abbab9c673487842f53Sam Judd } 4093a5750359cb0362d01d394032944854fdde4069eSam Judd } 4103a5750359cb0362d01d394032944854fdde4069eSam Judd 4113a5750359cb0362d01d394032944854fdde4069eSam Judd /** 4123a5750359cb0362d01d394032944854fdde4069eSam Judd * Writes Graphic Control Extension 4133a5750359cb0362d01d394032944854fdde4069eSam Judd */ 4142c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private void writeGraphicCtrlExt() throws IOException { 4153a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0x21); // extension introducer 4163a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0xf9); // GCE label 4173a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(4); // data block size 4183a5750359cb0362d01d394032944854fdde4069eSam Judd int transp, disp; 4192c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd if (transparent == null && !hasTransparentPixels) { 4203a5750359cb0362d01d394032944854fdde4069eSam Judd transp = 0; 4213a5750359cb0362d01d394032944854fdde4069eSam Judd disp = 0; // dispose = no action 4223a5750359cb0362d01d394032944854fdde4069eSam Judd } else { 4233a5750359cb0362d01d394032944854fdde4069eSam Judd transp = 1; 4243a5750359cb0362d01d394032944854fdde4069eSam Judd disp = 2; // force clear if using transparent color 4253a5750359cb0362d01d394032944854fdde4069eSam Judd } 4263a5750359cb0362d01d394032944854fdde4069eSam Judd if (dispose >= 0) { 4273a5750359cb0362d01d394032944854fdde4069eSam Judd disp = dispose & 7; // user override 4283a5750359cb0362d01d394032944854fdde4069eSam Judd } 4293a5750359cb0362d01d394032944854fdde4069eSam Judd disp <<= 2; 4303a5750359cb0362d01d394032944854fdde4069eSam Judd 4313a5750359cb0362d01d394032944854fdde4069eSam Judd // packed fields 4323a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0 | // 1:3 reserved 4333a5750359cb0362d01d394032944854fdde4069eSam Judd disp | // 4:6 disposal 4343a5750359cb0362d01d394032944854fdde4069eSam Judd 0 | // 7 user input - 0 = none 4353a5750359cb0362d01d394032944854fdde4069eSam Judd transp); // 8 transparency flag 4363a5750359cb0362d01d394032944854fdde4069eSam Judd 4373a5750359cb0362d01d394032944854fdde4069eSam Judd writeShort(delay); // delay x 1/100 sec 4383a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(transIndex); // transparent color index 4393a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0); // block terminator 4403a5750359cb0362d01d394032944854fdde4069eSam Judd } 4413a5750359cb0362d01d394032944854fdde4069eSam Judd 4423a5750359cb0362d01d394032944854fdde4069eSam Judd /** 4433a5750359cb0362d01d394032944854fdde4069eSam Judd * Writes Image Descriptor 4443a5750359cb0362d01d394032944854fdde4069eSam Judd */ 4452c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private void writeImageDesc() throws IOException { 4463a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0x2c); // image separator 4473a5750359cb0362d01d394032944854fdde4069eSam Judd writeShort(0); // image position x,y = 0,0 4483a5750359cb0362d01d394032944854fdde4069eSam Judd writeShort(0); 4493a5750359cb0362d01d394032944854fdde4069eSam Judd writeShort(width); // image size 4503a5750359cb0362d01d394032944854fdde4069eSam Judd writeShort(height); 4513a5750359cb0362d01d394032944854fdde4069eSam Judd // packed fields 4523a5750359cb0362d01d394032944854fdde4069eSam Judd if (firstFrame) { 4533a5750359cb0362d01d394032944854fdde4069eSam Judd // no LCT - GCT is used for first (or only) frame 4543a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0); 4553a5750359cb0362d01d394032944854fdde4069eSam Judd } else { 4563a5750359cb0362d01d394032944854fdde4069eSam Judd // specify normal LCT 4573a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0x80 | // 1 local color table 1=yes 4583a5750359cb0362d01d394032944854fdde4069eSam Judd 0 | // 2 interlace - 0=no 4593a5750359cb0362d01d394032944854fdde4069eSam Judd 0 | // 3 sorted - 0=no 4603a5750359cb0362d01d394032944854fdde4069eSam Judd 0 | // 4-5 reserved 4613a5750359cb0362d01d394032944854fdde4069eSam Judd palSize); // 6-8 size of color table 4623a5750359cb0362d01d394032944854fdde4069eSam Judd } 4633a5750359cb0362d01d394032944854fdde4069eSam Judd } 4643a5750359cb0362d01d394032944854fdde4069eSam Judd 4653a5750359cb0362d01d394032944854fdde4069eSam Judd /** 4663a5750359cb0362d01d394032944854fdde4069eSam Judd * Writes Logical Screen Descriptor 4673a5750359cb0362d01d394032944854fdde4069eSam Judd */ 4682c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private void writeLSD() throws IOException { 4693a5750359cb0362d01d394032944854fdde4069eSam Judd // logical screen size 4703a5750359cb0362d01d394032944854fdde4069eSam Judd writeShort(width); 4713a5750359cb0362d01d394032944854fdde4069eSam Judd writeShort(height); 4723a5750359cb0362d01d394032944854fdde4069eSam Judd // packed fields 4733a5750359cb0362d01d394032944854fdde4069eSam Judd out.write((0x80 | // 1 : global color table flag = 1 (gct used) 4743a5750359cb0362d01d394032944854fdde4069eSam Judd 0x70 | // 2-4 : color resolution = 7 4753a5750359cb0362d01d394032944854fdde4069eSam Judd 0x00 | // 5 : gct sort flag = 0 4763a5750359cb0362d01d394032944854fdde4069eSam Judd palSize)); // 6-8 : gct size 4773a5750359cb0362d01d394032944854fdde4069eSam Judd 4783a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0); // background color index 4793a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0); // pixel aspect ratio - assume 1:1 4803a5750359cb0362d01d394032944854fdde4069eSam Judd } 4813a5750359cb0362d01d394032944854fdde4069eSam Judd 4823a5750359cb0362d01d394032944854fdde4069eSam Judd /** 4833a5750359cb0362d01d394032944854fdde4069eSam Judd * Writes Netscape application extension to define repeat count. 4843a5750359cb0362d01d394032944854fdde4069eSam Judd */ 4852c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private void writeNetscapeExt() throws IOException { 4863a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0x21); // extension introducer 4873a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0xff); // app extension label 4883a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(11); // block size 4893a5750359cb0362d01d394032944854fdde4069eSam Judd writeString("NETSCAPE" + "2.0"); // app id + auth code 4903a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(3); // sub-block size 4913a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(1); // loop sub-block id 4923a5750359cb0362d01d394032944854fdde4069eSam Judd writeShort(repeat); // loop count (extra iterations, 0=repeat forever) 4933a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0); // block terminator 4943a5750359cb0362d01d394032944854fdde4069eSam Judd } 4953a5750359cb0362d01d394032944854fdde4069eSam Judd 4963a5750359cb0362d01d394032944854fdde4069eSam Judd /** 4973a5750359cb0362d01d394032944854fdde4069eSam Judd * Writes color table 4983a5750359cb0362d01d394032944854fdde4069eSam Judd */ 4992c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private void writePalette() throws IOException { 5003a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(colorTab, 0, colorTab.length); 5013a5750359cb0362d01d394032944854fdde4069eSam Judd int n = (3 * 256) - colorTab.length; 5023a5750359cb0362d01d394032944854fdde4069eSam Judd for (int i = 0; i < n; i++) { 5033a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(0); 5043a5750359cb0362d01d394032944854fdde4069eSam Judd } 5053a5750359cb0362d01d394032944854fdde4069eSam Judd } 5063a5750359cb0362d01d394032944854fdde4069eSam Judd 5073a5750359cb0362d01d394032944854fdde4069eSam Judd /** 5083a5750359cb0362d01d394032944854fdde4069eSam Judd * Encodes and writes pixel data 5093a5750359cb0362d01d394032944854fdde4069eSam Judd */ 5102c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private void writePixels() throws IOException { 5113a5750359cb0362d01d394032944854fdde4069eSam Judd LZWEncoder encoder = new LZWEncoder(width, height, indexedPixels, colorDepth); 5123a5750359cb0362d01d394032944854fdde4069eSam Judd encoder.encode(out); 5133a5750359cb0362d01d394032944854fdde4069eSam Judd } 5143a5750359cb0362d01d394032944854fdde4069eSam Judd 5153a5750359cb0362d01d394032944854fdde4069eSam Judd /** 5163a5750359cb0362d01d394032944854fdde4069eSam Judd * Write 16-bit value to output stream, LSB first 5173a5750359cb0362d01d394032944854fdde4069eSam Judd */ 5182c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private void writeShort(int value) throws IOException { 5193a5750359cb0362d01d394032944854fdde4069eSam Judd out.write(value & 0xff); 5203a5750359cb0362d01d394032944854fdde4069eSam Judd out.write((value >> 8) & 0xff); 5213a5750359cb0362d01d394032944854fdde4069eSam Judd } 5223a5750359cb0362d01d394032944854fdde4069eSam Judd 5233a5750359cb0362d01d394032944854fdde4069eSam Judd /** 5243a5750359cb0362d01d394032944854fdde4069eSam Judd * Writes string to output stream 5253a5750359cb0362d01d394032944854fdde4069eSam Judd */ 5262c259f532bee14a4f3f6be419bcfb58ef5e22ff5Sam Judd private void writeString(String s) throws IOException { 5273a5750359cb0362d01d394032944854fdde4069eSam Judd for (int i = 0; i < s.length(); i++) { 5283a5750359cb0362d01d394032944854fdde4069eSam Judd out.write((byte) s.charAt(i)); 5293a5750359cb0362d01d394032944854fdde4069eSam Judd } 5303a5750359cb0362d01d394032944854fdde4069eSam Judd } 5313a5750359cb0362d01d394032944854fdde4069eSam Judd} 532