Advertisement
Guest User

GifDecoder

a guest
Apr 4th, 2010
230
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 17.58 KB | None | 0 0
  1. import java.net.*;
  2. import java.io.*;
  3. import java.util.*;
  4. import java.awt.*;
  5. import java.awt.image.*;
  6.  
  7. /**
  8.  * Class GifDecoder - Decodes a GIF file into one or more frames.
  9.  * <br><pre>
  10.  * Example:
  11.  *    GifDecoder d = new GifDecoder();
  12.  *    d.read("sample.gif");
  13.  *    int n = d.getFrameCount();
  14.  *    for (int i = 0; i < n; i++) {
  15.  *       Image frame = d.getFrame(i);  // frame i
  16.  *       int t = d.getDelay(i);  // display duration of frame in milliseconds
  17.  *       // do something with frame
  18.  *    }
  19.  * </pre>
  20.  * No copyright asserted on the source code of this class.  May be used for
  21.  * any purpose, however, refer to the Unisys LZW patent for any additional
  22.  * restrictions.  Please forward any corrections to kweiner@fmsware.com.
  23.  *
  24.  * source : http://www.docjar.com/docs/api/com/fmsware/index.html
  25.  *
  26.  * @author Kevin Weiner, FM Software; LZW decoder adapted from John Cristy's ImageMagick.
  27.  * @version 1.01 July 2001
  28.  *
  29.  */
  30.  
  31. public final class GifDecoder
  32. {
  33.  
  34.     /**
  35.      * File read status: No errors.
  36.      */
  37.     public static final int STATUS_OK = 0;
  38.  
  39.     /**
  40.      * File read status: Error decoding file (may be partially decoded)
  41.      */
  42.     public static final int STATUS_FORMAT_ERROR = 1;
  43.  
  44.     /**
  45.      * File read status: Unable to open source.
  46.      */
  47.     public static final int STATUS_OPEN_ERROR = 2;
  48.  
  49.     protected BufferedInputStream in;
  50.     protected int status;
  51.  
  52.     protected int width;            // full image width
  53.     protected int height;           // full image height
  54.     protected boolean gctFlag;      // global color table used
  55.     protected int gctSize;          // size of global color table
  56.     protected int loopCount = 1;    // iterations; 0 = repeat forever
  57.  
  58.     protected int[] gct;            // global color table
  59.     protected int[] lct;            // local color table
  60.     protected int[] act;            // active color table
  61.  
  62.     protected int bgIndex;          // background color index
  63.     protected int bgColor;          // background color
  64.     protected int lastBgColor;      // previous bg color
  65.     protected int pixelAspect;      // pixel aspect ratio
  66.  
  67.     protected boolean lctFlag;      // local color table flag
  68.     protected boolean interlace;    // interlace flag
  69.     protected int lctSize;          // local color table size
  70.  
  71.     protected int ix, iy, iw, ih;   // current image rectangle
  72.     protected Rectangle lastRect;   // last image rect
  73.     protected int[] image;  // current frame
  74.     protected int[]lastImage;  // previous frame
  75.  
  76.     protected byte[] block = new byte[256];  // current data block
  77.     protected int blockSize = 0;    // block size
  78.  
  79.     // last graphic control extension info
  80.     protected int dispose = 0;   // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
  81.     protected int lastDispose = 0;
  82.     protected boolean transparency = false;   // use transparent color
  83.     protected int delay = 0;        // delay in milliseconds
  84.     protected int transIndex;       // transparent color index
  85.  
  86.     protected static final int MaxStackSize = 4096;   // max decoder pixel stack size
  87.  
  88.     // LZW decoder working arrays
  89.     protected short[] prefix;
  90.     protected byte[] suffix;
  91.     protected byte[] pixelStack;
  92.     protected byte[] pixels;
  93.  
  94.     protected Vector frames;     // frames read from current file
  95.     protected int frameCount;
  96.  
  97.     static class GifFrame
  98.     {
  99.         public GifFrame(int[] im, int del)
  100.         {
  101.             image = im;
  102.             delay = del;
  103.         }
  104.         public int[] image;
  105.         public int delay;
  106.     }
  107.  
  108.  
  109.     /**
  110.      * Gets display duration for specified frame.
  111.      *
  112.      * @param n int index of frame
  113.      * @return delay in milliseconds
  114.      */
  115.     public int getDelay(int n)
  116.     {
  117.         //
  118.         delay = -1;
  119.         if ((n >= 0) && (n < frameCount))
  120.             delay = ((GifFrame) frames.elementAt(n)).delay;
  121.         return delay;
  122.     }
  123.  
  124.  
  125.     /**
  126.      * Gets the image contents of frame n.
  127.      *
  128.      * @return Image representation of frame, or null if n is invalid.
  129.      */
  130.     public Image getFrame(int n)
  131.     {
  132.         Image im = null;
  133.         if ((n >= 0) && (n < frameCount))
  134.             im = createImage(getImageSourceFrame(n));
  135.         return im;
  136.     }
  137.  
  138.     private int[] getImageSourceFrame(int n)
  139.     {
  140.         return ((GifFrame) frames.elementAt(n)).image;
  141.     }
  142.  
  143.     private Image createImage(int[] src)
  144.     {
  145.         MemoryImageSource mem = new MemoryImageSource(width,height,src,0,width);
  146.         return Toolkit.getDefaultToolkit().createImage(mem);
  147.     }
  148.  
  149.  
  150.     /**
  151.      * Gets the number of frames read from file.
  152.      * @return frame count
  153.      */
  154.     public int getFrameCount()
  155.     {
  156.         return frameCount;
  157.     }
  158.  
  159.  
  160.     /**
  161.      * Gets the first (or only) image read.
  162.      *
  163.      * @return Image containing first frame, or null if none.
  164.      */
  165.     public Image getImage()
  166.     {
  167.         return getFrame(0);
  168.     }
  169.  
  170.  
  171.     /**
  172.      * Gets the "Netscape" iteration count, if any.
  173.      * A count of 0 means repeat indefinitiely.
  174.      *
  175.      * @return iteration count if one was specified, else 1.
  176.      */
  177.     public int getLoopCount()
  178.     {
  179.         return loopCount;
  180.     }
  181.  
  182.  
  183.     /**
  184.      * Reads GIF image from stream
  185.      *
  186.      * @param BufferedInputStream containing GIF file.
  187.      * @return read status code (0 = no errors)
  188.      */
  189.     public int read(BufferedInputStream is)
  190.     {
  191.         init();
  192.         if (is != null)
  193.         {
  194.             in = is;
  195.             readHeader();
  196.             if (!err())
  197.             {
  198.                 readContents();
  199.                 if (frameCount < 0)
  200.                     status = STATUS_FORMAT_ERROR;
  201.             }
  202.         } else
  203.         {
  204.             status = STATUS_OPEN_ERROR;
  205.         }
  206.         try
  207.         {
  208.             is.close();
  209.         } catch (IOException e)
  210.         {}
  211.         return status;
  212.     }
  213.  
  214.  
  215.     /**
  216.      * Reads GIF file from specified source (file or URL string)
  217.      *
  218.      * @param name String containing source
  219.      * @return read status code (0 = no errors)
  220.      */
  221.     public int read(String name)
  222.     {
  223.         status = STATUS_OK;
  224.         try
  225.         {
  226.             name = name.trim();
  227.             if (name.indexOf("://") > 0)
  228.             {
  229.                 URL url = new URL(name);
  230.                 in = new BufferedInputStream(url.openStream());
  231.             } else
  232.             {
  233.                 in = new BufferedInputStream(new FileInputStream(name));
  234.             }
  235.             status = read(in);
  236.         } catch (IOException e)
  237.         {
  238.             status = STATUS_OPEN_ERROR;
  239.         }
  240.  
  241.         return status;
  242.     }
  243.  
  244.  
  245.     /**
  246.      * Decodes LZW image data into pixel array.
  247.      * Adapted from John Cristy's ImageMagick.
  248.      */
  249.     protected void decodeImageData()
  250.     {
  251.         int NullCode = -1;
  252.         int npix = iw * ih;
  253.         int available, clear, code_mask, code_size, end_of_information, in_code, old_code,
  254.         bits, code, count, i, datum, data_size, first, top, bi, pi;
  255.  
  256.         if ((pixels == null) || (pixels.length < npix))
  257.             pixels = new byte[npix];    // allocate new pixel array
  258.  
  259.         if (prefix == null)
  260.             prefix = new short[MaxStackSize];
  261.         if (suffix == null)
  262.             suffix = new byte[MaxStackSize];
  263.         if (pixelStack == null)
  264.             pixelStack = new byte[MaxStackSize+1];
  265.  
  266.  
  267.         //  Initialize GIF data stream decoder.
  268.  
  269.         data_size = read();
  270.         clear = 1 << data_size;
  271.         end_of_information = clear + 1;
  272.         available = clear + 2;
  273.         old_code = NullCode;
  274.         code_size = data_size + 1;
  275.         code_mask = (1 << code_size) - 1;
  276.         for (code = 0; code < clear; code++)
  277.         {
  278.             prefix[code] = 0;
  279.             suffix[code] = (byte) code;
  280.         }
  281.  
  282.         //  Decode GIF pixel stream.
  283.  
  284.         datum = bits = count = first = top = pi = bi = 0;
  285.  
  286.         for (i = 0; i < npix; )
  287.         {
  288.             if (top == 0)
  289.             {
  290.                 if (bits < code_size)
  291.                 {
  292.                     //  Load bytes until there are enough bits for a code.
  293.                     if (count == 0)
  294.                     {
  295.                         // Read a new data block.
  296.                         count = readBlock();
  297.                         if (count <= 0)
  298.                             break;
  299.                         bi = 0;
  300.                     }
  301.                     datum += (((int) block[bi]) & 0xff) << bits;
  302.                     bits += 8;
  303.                     bi++;
  304.                     count--;
  305.                     continue;
  306.                 }
  307.  
  308.                 //  Get the next code.
  309.  
  310.                 code = datum & code_mask;
  311.                 datum >>= code_size;
  312.                 bits -= code_size;
  313.  
  314.                 //  Interpret the code
  315.  
  316.                 if ((code > available) || (code == end_of_information))
  317.                     break;
  318.                 if (code == clear)
  319.                 {
  320.                     //  Reset decoder.
  321.                     code_size = data_size + 1;
  322.                     code_mask = (1 << code_size) - 1;
  323.                     available = clear + 2;
  324.                     old_code = NullCode;
  325.                     continue;
  326.                 }
  327.                 if (old_code == NullCode)
  328.                 {
  329.                     pixelStack[top++] = suffix[code];
  330.                     old_code = code;
  331.                     first = code;
  332.                     continue;
  333.                 }
  334.                 in_code = code;
  335.                 if (code == available)
  336.                 {
  337.                     pixelStack[top++] = (byte) first;
  338.                     code = old_code;
  339.                 }
  340.                 while (code > clear)
  341.                 {
  342.                     pixelStack[top++] = suffix[code];
  343.                     code = prefix[code];
  344.                 }
  345.                 first = ((int) suffix[code]) & 0xff;
  346.  
  347.                 //  Add a new string to the string table,
  348.  
  349.                 if (available >= MaxStackSize)
  350.                     break;
  351.                 pixelStack[top++] = (byte) first;
  352.                 prefix[available] = (short) old_code;
  353.                 suffix[available] = (byte) first;
  354.                 available++;
  355.                 if (((available & code_mask) == 0) && (available < MaxStackSize))
  356.                 {
  357.                     code_size++;
  358.                     code_mask += available;
  359.                 }
  360.                 old_code = in_code;
  361.             }
  362.  
  363.             //  Pop a pixel off the pixel stack.
  364.  
  365.             top--;
  366.             pixels[pi++] = pixelStack[top];
  367.             i++;
  368.         }
  369.  
  370.         for (i = pi; i < npix; i++)
  371.             pixels[i] = 0;  // clear missing pixels
  372.  
  373.     }
  374.  
  375.  
  376.     /**
  377.      * Returns true if an error was encountered during reading/decoding
  378.      */
  379.     protected boolean err()
  380.     {
  381.         return status != STATUS_OK;
  382.     }
  383.  
  384.  
  385.     /**
  386.      * Initializes or re-initializes reader
  387.      */
  388.     protected void init()
  389.     {
  390.         status = STATUS_OK;
  391.         frameCount = 0;
  392.         frames = new Vector();
  393.         gct = null;
  394.         lct = null;
  395.     }
  396.  
  397.  
  398.     /**
  399.      * Reads a single byte from the input stream.
  400.      */
  401.     protected int read()
  402.     {
  403.         int curByte = 0;
  404.         try
  405.         {
  406.             curByte = in.read();
  407.         } catch (IOException e)
  408.         {
  409.             status = STATUS_FORMAT_ERROR;
  410.         }
  411.         return curByte;
  412.     }
  413.  
  414.  
  415.     /**
  416.      * Reads next variable length block from input.
  417.      *
  418.      * @return number of bytes stored in "buffer"
  419.      */
  420.     protected int readBlock()
  421.     {
  422.         blockSize = read();
  423.         int n = 0;
  424.         if (blockSize > 0)
  425.         {
  426.             try
  427.             {
  428.                 int count = 0;
  429.                 while (n<blockSize)
  430.                 {
  431.                     count = in.read(block, n, blockSize-n);
  432.                     if (count==-1)
  433.                         break;
  434.                     n += count;
  435.                 }
  436.             } catch (IOException e)
  437.             {}
  438.  
  439.             if (n < blockSize)
  440.                 status = STATUS_FORMAT_ERROR;
  441.         }
  442.         return n;
  443.     }
  444.  
  445.  
  446.     /**
  447.      * Reads color table as 256 RGB integer values
  448.      *
  449.      * @param ncolors int number of colors to read
  450.      * @return int array containing 256 colors (packed ARGB with full alpha)
  451.      */
  452.     protected int[] readColorTable(int ncolors)
  453.     {
  454.         int nbytes = 3*ncolors;
  455.         int[] tab = null;
  456.         byte[] c = new byte[nbytes];
  457.         int n = 0;
  458.         try
  459.         {
  460.             n = in.read(c);
  461.         } catch (IOException e)
  462.         {}
  463.         if (n < nbytes)
  464.             status = STATUS_FORMAT_ERROR;
  465.         else
  466.         {
  467.             tab = new int[256];  // max size to avoid bounds checks
  468.             int i = 0;
  469.             int j = 0;
  470.             while (i < ncolors)
  471.             {
  472.                 int r = ((int) c[j++]) & 0xff;
  473.                 int g = ((int) c[j++]) & 0xff;
  474.                 int b = ((int) c[j++]) & 0xff;
  475.                 tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
  476.             }
  477.         }
  478.         return tab;
  479.     }
  480.  
  481.  
  482.     /**
  483.      * Main file parser.  Reads GIF content blocks.
  484.      */
  485.     protected void readContents()
  486.     {
  487.         // read GIF file content blocks
  488.         boolean done = false;
  489.         while (!(done || err()))
  490.         {
  491.             int code = read();
  492.             switch (code)
  493.             {
  494.  
  495.                 case 0x2C:    // image separator
  496.                     readImage();
  497.                     break;
  498.  
  499.                 case 0x21:    // extension
  500.                     code = read();
  501.                     switch (code)
  502.                     {
  503.  
  504.                         case 0xf9:    // graphics control extension
  505.                             readGraphicControlExt();
  506.                             break;
  507.  
  508.                         case 0xff:    // application extension
  509.                             readBlock();
  510.                             String app = "";
  511.                             for (int i = 0; i < 11; i++)
  512.                                 app += (char) block[i];
  513.                             if (app.equals("NETSCAPE2.0"))
  514.                                 readNetscapeExt();
  515.                             else
  516.                                 skip();        // don't care
  517.                             break;
  518.  
  519.                         default:    // uninteresting extension
  520.                             skip();
  521.                     }
  522.                     break;
  523.  
  524.                 case 0x3b:        // terminator
  525.                     done = true;
  526.                     break;
  527.  
  528.                 default:
  529.                     status = STATUS_FORMAT_ERROR;
  530.             }
  531.         }
  532.     }
  533.  
  534.  
  535.     /**
  536.      * Reads Graphics Control Extension values
  537.      */
  538.     protected void readGraphicControlExt()
  539.     {
  540.         read();    // block size
  541.         int packed = read();   // packed fields
  542.         dispose = (packed & 0x1c) >> 2;   // disposal method
  543.         if (dispose == 0)
  544.             dispose = 1;   // elect to keep old image if discretionary
  545.         transparency = (packed & 1) != 0;
  546.         delay = readShort() * 10;   // delay in milliseconds
  547.         transIndex = read();        // transparent color index
  548.         read();                     // block terminator
  549.     }
  550.  
  551.  
  552.     /**
  553.      * Reads GIF file header information.
  554.      */
  555.     protected void readHeader()
  556.     {
  557.         String id = "";
  558.         for (int i = 0; i < 6; i++)
  559.             id += (char) read();
  560.         if (!id.startsWith("GIF"))
  561.         {
  562.             status = STATUS_FORMAT_ERROR;
  563.             return;
  564.         }
  565.  
  566.         readLSD();
  567.         if (gctFlag && !err())
  568.         {
  569.             gct = readColorTable(gctSize);
  570.             bgColor = gct[bgIndex];
  571.         }
  572.     }
  573.  
  574.  
  575.     /**
  576.      * Reads next frame image
  577.      */
  578.     protected void readImage()
  579.     {
  580.         ix = readShort();    // (sub)image position & size
  581.         iy = readShort();
  582.         iw = readShort();
  583.         ih = readShort();
  584.  
  585.         int packed = read();
  586.         lctFlag = (packed & 0x80) != 0;     // 1 - local color table flag
  587.         interlace = (packed & 0x40) != 0;   // 2 - interlace flag
  588.         // 3 - sort flag
  589.         // 4-5 - reserved
  590.         lctSize = 2 << (packed & 7);        // 6-8 - local color table size
  591.  
  592.         if (lctFlag)
  593.         {
  594.             lct = readColorTable(lctSize);   // read table
  595.             act = lct;    // make local table active
  596.         } else
  597.         {
  598.             act = gct;    // make global table active
  599.             if (bgIndex == transIndex)
  600.                 bgColor = 0;
  601.         }
  602.         int save = 0;
  603.         if (transparency)
  604.         {
  605.             save = act[transIndex];
  606.             act[transIndex] = 0;    // set transparent color if specified
  607.         }
  608.  
  609.         if (act == null)
  610.             status = STATUS_FORMAT_ERROR;   // no color table defined
  611.  
  612.         if (err()) return;
  613.  
  614.         decodeImageData();   // decode pixel data
  615.         skip();
  616.  
  617.         if (err()) return;
  618.  
  619.         frameCount++;
  620.  
  621.         // create new image to receive frame data
  622.  
  623.         image = getPixels();
  624.  
  625.         //image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
  626.  
  627.         //setPixels();   // transfer pixel data to image
  628.  
  629.         frames.addElement(new GifFrame(image, delay));   // add image to frame list
  630.  
  631.         if (transparency)
  632.             act[transIndex] = save;
  633.         resetFrame();
  634.  
  635.     }
  636.  
  637.  
  638.     /**
  639.      * Reads Logical Screen Descriptor
  640.      */
  641.     protected void readLSD()
  642.     {
  643.  
  644.         // logical screen size
  645.         width = readShort();
  646.         height = readShort();
  647.  
  648.         // packed fields
  649.         int packed = read();
  650.         gctFlag = (packed & 0x80) != 0;      // 1   : global color table flag
  651.         // 2-4 : color resolution
  652.         // 5   : gct sort flag
  653.         gctSize    = 2 << (packed & 7);      // 6-8 : gct size
  654.  
  655.         bgIndex = read();        // background color index
  656.         pixelAspect = read();    // pixel aspect ratio
  657.     }
  658.  
  659.  
  660.     /**
  661.      * Reads Netscape extenstion to obtain iteration count
  662.      */
  663.     protected void readNetscapeExt()
  664.     {
  665.         do
  666.         {
  667.             readBlock();
  668.             if (block[0] == 1)
  669.             {
  670.                 // loop count sub-block
  671.                 int b1 = ((int) block[1]) & 0xff;
  672.                 int b2 = ((int) block[2]) & 0xff;
  673.                 loopCount = (b2 << 8) | b1;
  674.             }
  675.         } while ((blockSize > 0) && !err());
  676.     }
  677.  
  678.  
  679.     /**
  680.      * Reads next 16-bit value, LSB first
  681.      */
  682.     protected int readShort()
  683.     {
  684.         // read 16-bit value, LSB first
  685.         return read() | (read() << 8);
  686.     }
  687.  
  688.  
  689.     /**
  690.      * Resets frame state for reading next image.
  691.      */
  692.     protected void resetFrame()
  693.     {
  694.         lastDispose = dispose;
  695.         lastRect = new Rectangle(ix, iy, iw, ih);
  696.         lastImage = image;
  697.         lastBgColor = bgColor;
  698.         dispose = 0;
  699.         transparency = false;
  700.         delay = 0;
  701.         lct = null;
  702.     }
  703.  
  704.  
  705.     /**
  706.      * Creates new frame image from current data (and previous
  707.      * frames as specified by their disposition codes).
  708.      */
  709.     protected int[] getPixels()
  710.     {
  711.         // expose destination image's pixels as int array
  712.         int[] dest = new int[width*height];//((DataBufferInt) image.getRaster().getDataBuffer()).getData();
  713.  
  714.         // fill in starting image contents based on last image's dispose code
  715.         if (lastDispose > 0)
  716.         {
  717.             if (lastDispose == 3)
  718.             {
  719.                 // use image before last
  720.                 int n = frameCount - 2;
  721.                 if (n > 0)
  722.                     lastImage = getImageSourceFrame(n-1);
  723.                 else
  724.                     lastImage = null;
  725.             }
  726.  
  727.             if (lastImage != null)
  728.             {
  729.                 int[] prev = lastImage;
  730.                 System.arraycopy(prev, 0, dest, 0, width*height);  // copy pixels
  731.  
  732.                 if (lastDispose == 2)
  733.                 {
  734.                     // fill last image rect area with background color
  735.                     int c = encodeColor(0,0,0);
  736.  
  737.                     if (!transparency)
  738.                         c = encodeColor(new Color(lastBgColor));   // use given background color
  739.  
  740.                     for(int i=0;i<image.length;i++)
  741.                     {
  742.                         image[i] = c;
  743.                     }
  744.                 }
  745.             }
  746.         }
  747.  
  748.         // copy each source line to the appropriate place in the destination
  749.         int pass = 1;
  750.         int inc = 8;
  751.         int iline = 0;
  752.         for (int i = 0; i < ih; i++)
  753.         {
  754.             int line = i;
  755.             if (interlace)
  756.             {
  757.                 if (iline >= ih)
  758.                 {
  759.                     pass++;
  760.                     switch (pass)
  761.                     {
  762.                         case 2:
  763.                             iline = 4;
  764.                             break;
  765.                         case 3:
  766.                             iline = 2;
  767.                             inc = 4;
  768.                             break;
  769.                         case 4:
  770.                             iline = 1;
  771.                             inc = 2;
  772.                     }
  773.                 }
  774.                 line = iline;
  775.                 iline += inc;
  776.             }
  777.             line += iy;
  778.             if (line < height)
  779.             {
  780.                 int k = line * width;
  781.                 int dx = k + ix;          // start of line in dest
  782.                 int dlim = dx + iw;       // end of dest line
  783.                 if ((k + width) < dlim)
  784.                     dlim = k + width;      // past dest edge
  785.                 int sx = i * iw;          // start of line in source
  786.                 while (dx < dlim)
  787.                 {
  788.                     // map color and insert in destination
  789.                     int index = ((int) pixels[sx++]) & 0xff;
  790.                     int c = act[index];
  791.                     if (c != 0)
  792.                     {
  793.                         dest[dx] = c;
  794.                     }
  795.                     dx++;
  796.                 }
  797.             }
  798.         }
  799.         return dest;
  800.     }
  801.  
  802.     public static int encodeColor(Color c)
  803.     {
  804.         return encodeColor(c.getRed(), c.getGreen(), c.getBlue());
  805.     }
  806.  
  807.     public static int encodeColor(int r, int g, int b)
  808.     {
  809.         int result = (r<<8)+(g<<4)+(b);
  810.  
  811.         return result;
  812.     }
  813.  
  814.  
  815.     /**
  816.      * Skips variable length blocks up to and including
  817.      * next zero length block.
  818.      */
  819.     protected void skip()
  820.     {
  821.         do
  822.         {
  823.             readBlock();
  824.         } while ((blockSize > 0) && !err());
  825.     }
  826. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement