Save This Page
Home » jcommon-1.0.13 » com.keypoint » [javadoc | source]
    1   package com.keypoint;
    2   
    3   import java.awt.Image;
    4   import java.awt.image.ImageObserver;
    5   import java.awt.image.PixelGrabber;
    6   import java.io.ByteArrayOutputStream;
    7   import java.io.IOException;
    8   import java.util.zip.CRC32;
    9   import java.util.zip.Deflater;
   10   import java.util.zip.DeflaterOutputStream;
   11   
   12   /**
   13    * PngEncoder takes a Java Image object and creates a byte string which can be 
   14    * saved as a PNG file.  The Image is presumed to use the DirectColorModel.
   15    *
   16    * <p>Thanks to Jay Denny at KeyPoint Software
   17    *    http://www.keypoint.com/
   18    * who let me develop this code on company time.</p>
   19    *
   20    * <p>You may contact me with (probably very-much-needed) improvements,
   21    * comments, and bug fixes at:</p>
   22    *
   23    *   <p><code>david@catcode.com</code></p>
   24    *
   25    * <p>This library is free software; you can redistribute it and/or
   26    * modify it under the terms of the GNU Lesser General Public
   27    * License as published by the Free Software Foundation; either
   28    * version 2.1 of the License, or (at your option) any later version.</p>
   29    *
   30    * <p>This library is distributed in the hope that it will be useful,
   31    * but WITHOUT ANY WARRANTY; without even the implied warranty of
   32    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   33    * Lesser General Public License for more details.</p>
   34    *
   35    * <p>You should have received a copy of the GNU Lesser General Public
   36    * License along with this library; if not, write to the Free Software
   37    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
   38    * USA. A copy of the GNU LGPL may be found at
   39    * <code>http://www.gnu.org/copyleft/lesser.html</code></p>
   40    *
   41    * @author J. David Eisenberg
   42    * @version 1.5, 19 Oct 2003
   43    *
   44    * CHANGES:
   45    * --------
   46    * 19-Nov-2002 : CODING STYLE CHANGES ONLY (by David Gilbert for Object 
   47    *               Refinery Limited);
   48    * 19-Sep-2003 : Fix for platforms using EBCDIC (contributed by Paulo Soares);
   49    * 19-Oct-2003 : Change private fields to protected fields so that
   50    *               PngEncoderB can inherit them (JDE)
   51    *               Fixed bug with calculation of nRows
   52    */
   53   
   54   public class PngEncoder {
   55   
   56       /** Constant specifying that alpha channel should be encoded. */
   57       public static final boolean ENCODE_ALPHA = true;
   58   
   59       /** Constant specifying that alpha channel should not be encoded. */
   60       public static final boolean NO_ALPHA = false;
   61   
   62       /** Constants for filter (NONE). */
   63       public static final int FILTER_NONE = 0;
   64   
   65       /** Constants for filter (SUB). */
   66       public static final int FILTER_SUB = 1;
   67   
   68       /** Constants for filter (UP). */
   69       public static final int FILTER_UP = 2;
   70   
   71       /** Constants for filter (LAST). */
   72       public static final int FILTER_LAST = 2;
   73       
   74       /** IHDR tag. */
   75       protected static final byte[] IHDR = {73, 72, 68, 82};
   76       
   77       /** IDAT tag. */
   78       protected static final byte[] IDAT = {73, 68, 65, 84};
   79       
   80       /** IEND tag. */
   81       protected static final byte[] IEND = {73, 69, 78, 68};
   82   
   83       protected static final byte[] PHYS = {(byte)'p', (byte)'H', (byte)'Y', (byte)'s'};
   84   
   85       /** The png bytes. */
   86       protected byte[] pngBytes;
   87   
   88       /** The prior row. */
   89       protected byte[] priorRow;
   90   
   91       /** The left bytes. */
   92       protected byte[] leftBytes;
   93   
   94       /** The image. */
   95       protected Image image;
   96   
   97       /** The width. */
   98       protected int width;
   99   
  100       /** The height. */
  101       protected int height;
  102   
  103       /** The byte position. */
  104       protected int bytePos;
  105   
  106       /** The maximum position. */
  107       protected int maxPos;
  108   
  109       /** CRC. */
  110       protected CRC32 crc = new CRC32();
  111   
  112       /** The CRC value. */
  113       protected long crcValue;
  114   
  115       /** Encode alpha? */
  116       protected boolean encodeAlpha;
  117   
  118       /** The filter type. */
  119       protected int filter;
  120   
  121       /** The bytes-per-pixel. */
  122       protected int bytesPerPixel;
  123   
  124       /** The physical pixel dimension : number of pixels per inch on the X axis. */
  125       private int xDpi = 0;
  126   
  127       /** The physical pixel dimension : number of pixels per inch on the Y axis. */
  128       private int yDpi = 0;
  129   
  130       /** Used for conversion of DPI to Pixels per Meter. */
  131       static private float INCH_IN_METER_UNIT = 0.0254f;
  132   
  133       /** 
  134        * The compression level (1 = best speed, 9 = best compression, 
  135        * 0 = no compression). 
  136        */
  137       protected int compressionLevel;
  138   
  139       /**
  140        * Class constructor.
  141        */
  142       public PngEncoder() {
  143           this(null, false, FILTER_NONE, 0);
  144       }
  145   
  146       /**
  147        * Class constructor specifying Image to encode, with no alpha channel 
  148        * encoding.
  149        *
  150        * @param image A Java Image object which uses the DirectColorModel
  151        * @see java.awt.Image
  152        */
  153       public PngEncoder(Image image) {
  154           this(image, false, FILTER_NONE, 0);
  155       }
  156   
  157       /**
  158        * Class constructor specifying Image to encode, and whether to encode 
  159        * alpha.
  160        *
  161        * @param image A Java Image object which uses the DirectColorModel
  162        * @param encodeAlpha Encode the alpha channel? false=no; true=yes
  163        * @see java.awt.Image
  164        */
  165       public PngEncoder(Image image, boolean encodeAlpha) {
  166           this(image, encodeAlpha, FILTER_NONE, 0);
  167       }
  168   
  169       /**
  170        * Class constructor specifying Image to encode, whether to encode alpha, 
  171        * and filter to use.
  172        *
  173        * @param image A Java Image object which uses the DirectColorModel
  174        * @param encodeAlpha Encode the alpha channel? false=no; true=yes
  175        * @param whichFilter 0=none, 1=sub, 2=up
  176        * @see java.awt.Image
  177        */
  178       public PngEncoder(Image image, boolean encodeAlpha, int whichFilter) {
  179           this(image, encodeAlpha, whichFilter, 0);
  180       }
  181   
  182   
  183       /**
  184        * Class constructor specifying Image source to encode, whether to encode 
  185        * alpha, filter to use, and compression level.
  186        *
  187        * @param image A Java Image object
  188        * @param encodeAlpha Encode the alpha channel? false=no; true=yes
  189        * @param whichFilter 0=none, 1=sub, 2=up
  190        * @param compLevel 0..9 (1 = best speed, 9 = best compression, 0 = no 
  191        *        compression)
  192        * @see java.awt.Image
  193        */
  194       public PngEncoder(Image image, boolean encodeAlpha, int whichFilter, 
  195               int compLevel) {
  196           this.image = image;
  197           this.encodeAlpha = encodeAlpha;
  198           setFilter(whichFilter);
  199           if (compLevel >= 0 && compLevel <= 9) {
  200               this.compressionLevel = compLevel;
  201           }
  202       }
  203   
  204       /**
  205        * Set the image to be encoded.
  206        *
  207        * @param image A Java Image object which uses the DirectColorModel
  208        * @see java.awt.Image
  209        * @see java.awt.image.DirectColorModel
  210        */
  211       public void setImage(Image image) {
  212           this.image = image;
  213           this.pngBytes = null;
  214       }
  215   
  216       /**
  217        * Returns the image to be encoded.
  218        */
  219       public Image getImage() {
  220         return image;
  221       }
  222   
  223     /**
  224        * Creates an array of bytes that is the PNG equivalent of the current 
  225        * image, specifying whether to encode alpha or not.
  226        *
  227        * @param encodeAlpha boolean false=no alpha, true=encode alpha
  228        * @return an array of bytes, or null if there was a problem
  229        */
  230       public byte[] pngEncode(boolean encodeAlpha) {
  231           byte[]  pngIdBytes = {-119, 80, 78, 71, 13, 10, 26, 10};
  232   
  233           if (this.image == null) {
  234               return null;
  235           }
  236           this.width = this.image.getWidth(null);
  237           this.height = this.image.getHeight(null);
  238   
  239           /*
  240            * start with an array that is big enough to hold all the pixels
  241            * (plus filter bytes), and an extra 200 bytes for header info
  242            */
  243           this.pngBytes = new byte[((this.width + 1) * this.height * 3) + 200];
  244   
  245           /*
  246            * keep track of largest byte written to the array
  247            */
  248           this.maxPos = 0;
  249   
  250           this.bytePos = writeBytes(pngIdBytes, 0);
  251           //hdrPos = bytePos;
  252           writeHeader();
  253           writeResolution();
  254           //dataPos = bytePos;
  255           if (writeImageData()) {
  256               writeEnd();
  257               this.pngBytes = resizeByteArray(this.pngBytes, this.maxPos);
  258           }
  259           else {
  260               this.pngBytes = null;
  261           }
  262           return this.pngBytes;
  263       }
  264   
  265       /**
  266        * Creates an array of bytes that is the PNG equivalent of the current 
  267        * image.  Alpha encoding is determined by its setting in the constructor.
  268        *
  269        * @return an array of bytes, or null if there was a problem
  270        */
  271       public byte[] pngEncode() {
  272           return pngEncode(this.encodeAlpha);
  273       }
  274   
  275       /**
  276        * Set the alpha encoding on or off.
  277        *
  278        * @param encodeAlpha  false=no, true=yes
  279        */
  280       public void setEncodeAlpha(boolean encodeAlpha) {
  281           this.encodeAlpha = encodeAlpha;
  282       }
  283   
  284       /**
  285        * Retrieve alpha encoding status.
  286        *
  287        * @return boolean false=no, true=yes
  288        */
  289       public boolean getEncodeAlpha() {
  290           return this.encodeAlpha;
  291       }
  292   
  293       /**
  294        * Set the filter to use.
  295        *
  296        * @param whichFilter from constant list
  297        */
  298       public void setFilter(int whichFilter) {
  299           this.filter = FILTER_NONE;
  300           if (whichFilter <= FILTER_LAST) {
  301               this.filter = whichFilter;
  302           }
  303       }
  304   
  305       /**
  306        * Retrieve filtering scheme.
  307        *
  308        * @return int (see constant list)
  309        */
  310       public int getFilter() {
  311           return this.filter;
  312       }
  313   
  314       /**
  315        * Set the compression level to use.
  316        *
  317        * @param level the compression level (1 = best speed, 9 = best compression,
  318        *        0 = no compression)
  319        */
  320       public void setCompressionLevel(int level) {
  321           if (level >= 0 && level <= 9) {
  322               this.compressionLevel = level;
  323           }
  324       }
  325   
  326       /**
  327        * Retrieve compression level.
  328        *
  329        * @return int (1 = best speed, 9 = best compression, 0 = no compression)
  330        */
  331       public int getCompressionLevel() {
  332           return this.compressionLevel;
  333       }
  334   
  335       /**
  336        * Increase or decrease the length of a byte array.
  337        *
  338        * @param array The original array.
  339        * @param newLength The length you wish the new array to have.
  340        * @return Array of newly desired length. If shorter than the
  341        *         original, the trailing elements are truncated.
  342        */
  343       protected byte[] resizeByteArray(byte[] array, int newLength) {
  344           byte[]  newArray = new byte[newLength];
  345           int     oldLength = array.length;
  346   
  347           System.arraycopy(array, 0, newArray, 0, Math.min(oldLength, newLength));
  348           return newArray;
  349       }
  350   
  351       /**
  352        * Write an array of bytes into the pngBytes array.
  353        * Note: This routine has the side effect of updating
  354        * maxPos, the largest element written in the array.
  355        * The array is resized by 1000 bytes or the length
  356        * of the data to be written, whichever is larger.
  357        *
  358        * @param data The data to be written into pngBytes.
  359        * @param offset The starting point to write to.
  360        * @return The next place to be written to in the pngBytes array.
  361        */
  362       protected int writeBytes(byte[] data, int offset) {
  363           this.maxPos = Math.max(this.maxPos, offset + data.length);
  364           if (data.length + offset > this.pngBytes.length) {
  365               this.pngBytes = resizeByteArray(this.pngBytes, this.pngBytes.length
  366                       + Math.max(1000, data.length));
  367           }
  368           System.arraycopy(data, 0, this.pngBytes, offset, data.length);
  369           return offset + data.length;
  370       }
  371   
  372       /**
  373        * Write an array of bytes into the pngBytes array, specifying number of 
  374        * bytes to write. Note: This routine has the side effect of updating
  375        * maxPos, the largest element written in the array.
  376        * The array is resized by 1000 bytes or the length
  377        * of the data to be written, whichever is larger.
  378        *
  379        * @param data The data to be written into pngBytes.
  380        * @param nBytes The number of bytes to be written.
  381        * @param offset The starting point to write to.
  382        * @return The next place to be written to in the pngBytes array.
  383        */
  384       protected int writeBytes(byte[] data, int nBytes, int offset) {
  385           this.maxPos = Math.max(this.maxPos, offset + nBytes);
  386           if (nBytes + offset > this.pngBytes.length) {
  387               this.pngBytes = resizeByteArray(this.pngBytes, this.pngBytes.length
  388                       + Math.max(1000, nBytes));
  389           }
  390           System.arraycopy(data, 0, this.pngBytes, offset, nBytes);
  391           return offset + nBytes;
  392       }
  393   
  394       /**
  395        * Write a two-byte integer into the pngBytes array at a given position.
  396        *
  397        * @param n The integer to be written into pngBytes.
  398        * @param offset The starting point to write to.
  399        * @return The next place to be written to in the pngBytes array.
  400        */
  401       protected int writeInt2(int n, int offset) {
  402           byte[] temp = {(byte) ((n >> 8) & 0xff), (byte) (n & 0xff)};
  403           return writeBytes(temp, offset);
  404       }
  405   
  406       /**
  407        * Write a four-byte integer into the pngBytes array at a given position.
  408        *
  409        * @param n The integer to be written into pngBytes.
  410        * @param offset The starting point to write to.
  411        * @return The next place to be written to in the pngBytes array.
  412        */
  413       protected int writeInt4(int n, int offset) {
  414           byte[] temp = {(byte) ((n >> 24) & 0xff),
  415                          (byte) ((n >> 16) & 0xff),
  416                          (byte) ((n >> 8) & 0xff),
  417                          (byte) (n & 0xff)};
  418           return writeBytes(temp, offset);
  419       }
  420   
  421       /**
  422        * Write a single byte into the pngBytes array at a given position.
  423        *
  424        * @param b The integer to be written into pngBytes.
  425        * @param offset The starting point to write to.
  426        * @return The next place to be written to in the pngBytes array.
  427        */
  428       protected int writeByte(int b, int offset) {
  429           byte[] temp = {(byte) b};
  430           return writeBytes(temp, offset);
  431       }
  432   
  433       /**
  434        * Write a PNG "IHDR" chunk into the pngBytes array.
  435        */
  436       protected void writeHeader() {
  437   
  438           int startPos = this.bytePos = writeInt4(13, this.bytePos);
  439           this.bytePos = writeBytes(IHDR, this.bytePos);
  440           this.width = this.image.getWidth(null);
  441           this.height = this.image.getHeight(null);
  442           this.bytePos = writeInt4(this.width, this.bytePos);
  443           this.bytePos = writeInt4(this.height, this.bytePos);
  444           this.bytePos = writeByte(8, this.bytePos); // bit depth
  445           this.bytePos = writeByte((this.encodeAlpha) ? 6 : 2, this.bytePos); 
  446               // direct model
  447           this.bytePos = writeByte(0, this.bytePos); // compression method
  448           this.bytePos = writeByte(0, this.bytePos); // filter method
  449           this.bytePos = writeByte(0, this.bytePos); // no interlace
  450           this.crc.reset();
  451           this.crc.update(this.pngBytes, startPos, this.bytePos - startPos);
  452           this.crcValue = this.crc.getValue();
  453           this.bytePos = writeInt4((int) this.crcValue, this.bytePos);
  454       }
  455   
  456       /**
  457        * Perform "sub" filtering on the given row.
  458        * Uses temporary array leftBytes to store the original values
  459        * of the previous pixels.  The array is 16 bytes long, which
  460        * will easily hold two-byte samples plus two-byte alpha.
  461        *
  462        * @param pixels The array holding the scan lines being built
  463        * @param startPos Starting position within pixels of bytes to be filtered.
  464        * @param width Width of a scanline in pixels.
  465        */
  466       protected void filterSub(byte[] pixels, int startPos, int width) {
  467           int offset = this.bytesPerPixel;
  468           int actualStart = startPos + offset;
  469           int nBytes = width * this.bytesPerPixel;
  470           int leftInsert = offset;
  471           int leftExtract = 0;
  472   
  473           for (int i = actualStart; i < startPos + nBytes; i++) {
  474               this.leftBytes[leftInsert] =  pixels[i];
  475               pixels[i] = (byte) ((pixels[i] - this.leftBytes[leftExtract])
  476                        % 256);
  477               leftInsert = (leftInsert + 1) % 0x0f;
  478               leftExtract = (leftExtract + 1) % 0x0f;
  479           }
  480       }
  481   
  482       /**
  483        * Perform "up" filtering on the given row.
  484        * Side effect: refills the prior row with current row
  485        *
  486        * @param pixels The array holding the scan lines being built
  487        * @param startPos Starting position within pixels of bytes to be filtered.
  488        * @param width Width of a scanline in pixels.
  489        */
  490       protected void filterUp(byte[] pixels, int startPos, int width) {
  491   
  492           final int nBytes = width * this.bytesPerPixel;
  493   
  494           for (int i = 0; i < nBytes; i++) {
  495               final byte currentByte = pixels[startPos + i];
  496               pixels[startPos + i] = (byte) ((pixels[startPos  + i] 
  497                       - this.priorRow[i]) % 256);
  498               this.priorRow[i] = currentByte;
  499           }
  500       }
  501   
  502       /**
  503        * Write the image data into the pngBytes array.
  504        * This will write one or more PNG "IDAT" chunks. In order
  505        * to conserve memory, this method grabs as many rows as will
  506        * fit into 32K bytes, or the whole image; whichever is less.
  507        *
  508        *
  509        * @return true if no errors; false if error grabbing pixels
  510        */
  511       protected boolean writeImageData() {
  512           int rowsLeft = this.height;  // number of rows remaining to write
  513           int startRow = 0;       // starting row to process this time through
  514           int nRows;              // how many rows to grab at a time
  515   
  516           byte[] scanLines;       // the scan lines to be compressed
  517           int scanPos;            // where we are in the scan lines
  518           int startPos;           // where this line's actual pixels start (used
  519                                   // for filtering)
  520   
  521           byte[] compressedLines; // the resultant compressed lines
  522           int nCompressed;        // how big is the compressed area?
  523   
  524           //int depth;              // color depth ( handle only 8 or 32 )
  525   
  526           PixelGrabber pg;
  527   
  528           this.bytesPerPixel = (this.encodeAlpha) ? 4 : 3;
  529   
  530           Deflater scrunch = new Deflater(this.compressionLevel);
  531           ByteArrayOutputStream outBytes = new ByteArrayOutputStream(1024);
  532   
  533           DeflaterOutputStream compBytes = new DeflaterOutputStream(outBytes, 
  534                   scrunch);
  535           try {
  536               while (rowsLeft > 0) {
  537                   nRows = Math.min(32767 / (this.width 
  538                           * (this.bytesPerPixel + 1)), rowsLeft);
  539                   nRows = Math.max(nRows, 1);
  540   
  541                   int[] pixels = new int[this.width * nRows];
  542   
  543                   pg = new PixelGrabber(this.image, 0, startRow,
  544                           this.width, nRows, pixels, 0, this.width);
  545                   try {
  546                       pg.grabPixels();
  547                   }
  548                   catch (Exception e) {
  549                       System.err.println("interrupted waiting for pixels!");
  550                       return false;
  551                   }
  552                   if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
  553                       System.err.println("image fetch aborted or errored");
  554                       return false;
  555                   }
  556   
  557                   /*
  558                    * Create a data chunk. scanLines adds "nRows" for
  559                    * the filter bytes.
  560                    */
  561                   scanLines = new byte[this.width * nRows * this.bytesPerPixel 
  562                                        + nRows];
  563   
  564                   if (this.filter == FILTER_SUB) {
  565                       this.leftBytes = new byte[16];
  566                   }
  567                   if (this.filter == FILTER_UP) {
  568                       this.priorRow = new byte[this.width * this.bytesPerPixel];
  569                   }
  570   
  571                   scanPos = 0;
  572                   startPos = 1;
  573                   for (int i = 0; i < this.width * nRows; i++) {
  574                       if (i % this.width == 0) {
  575                           scanLines[scanPos++] = (byte) this.filter;
  576                           startPos = scanPos;
  577                       }
  578                       scanLines[scanPos++] = (byte) ((pixels[i] >> 16) & 0xff);
  579                       scanLines[scanPos++] = (byte) ((pixels[i] >>  8) & 0xff);
  580                       scanLines[scanPos++] = (byte) ((pixels[i]) & 0xff);
  581                       if (this.encodeAlpha) {
  582                           scanLines[scanPos++] = (byte) ((pixels[i] >> 24) 
  583                                   & 0xff);
  584                       }
  585                       if ((i % this.width == this.width - 1) 
  586                               && (this.filter != FILTER_NONE)) {
  587                           if (this.filter == FILTER_SUB) {
  588                               filterSub(scanLines, startPos, this.width);
  589                           }
  590                           if (this.filter == FILTER_UP) {
  591                               filterUp(scanLines, startPos, this.width);
  592                           }
  593                       }
  594                   }
  595   
  596                   /*
  597                    * Write these lines to the output area
  598                    */
  599                   compBytes.write(scanLines, 0, scanPos);
  600   
  601                   startRow += nRows;
  602                   rowsLeft -= nRows;
  603               }
  604               compBytes.close();
  605   
  606               /*
  607                * Write the compressed bytes
  608                */
  609               compressedLines = outBytes.toByteArray();
  610               nCompressed = compressedLines.length;
  611   
  612               this.crc.reset();
  613               this.bytePos = writeInt4(nCompressed, this.bytePos);
  614               this.bytePos = writeBytes(IDAT, this.bytePos);
  615               this.crc.update(IDAT);
  616               this.bytePos = writeBytes(compressedLines, nCompressed, 
  617                       this.bytePos);
  618               this.crc.update(compressedLines, 0, nCompressed);
  619   
  620               this.crcValue = this.crc.getValue();
  621               this.bytePos = writeInt4((int) this.crcValue, this.bytePos);
  622               scrunch.finish();
  623               return true;
  624           }
  625           catch (IOException e) {
  626               System.err.println(e.toString());
  627               return false;
  628           }
  629       }
  630   
  631       /**
  632        * Write a PNG "IEND" chunk into the pngBytes array.
  633        */
  634       protected void writeEnd() {
  635           this.bytePos = writeInt4(0, this.bytePos);
  636           this.bytePos = writeBytes(IEND, this.bytePos);
  637           this.crc.reset();
  638           this.crc.update(IEND);
  639           this.crcValue = this.crc.getValue();
  640           this.bytePos = writeInt4((int) this.crcValue, this.bytePos);
  641       }
  642   
  643   
  644       /**
  645        * Set the DPI for the X axis.
  646        *
  647        * @param xDpi  The number of dots per inch
  648        */
  649       public void setXDpi(int xDpi) {
  650           this.xDpi = Math.round(xDpi / INCH_IN_METER_UNIT);
  651   
  652       }
  653   
  654       /**
  655        * Get the DPI for the X axis.
  656        *
  657        * @return The number of dots per inch
  658        */
  659       public int getXDpi() {
  660           return Math.round(xDpi * INCH_IN_METER_UNIT);
  661       }
  662   
  663       /**
  664        * Set the DPI for the Y axis.
  665        *
  666        * @param yDpi  The number of dots per inch
  667        */
  668       public void setYDpi(int yDpi) {
  669           this.yDpi = Math.round(yDpi / INCH_IN_METER_UNIT);
  670       }
  671   
  672       /**
  673        * Get the DPI for the Y axis.
  674        *
  675        * @return The number of dots per inch
  676        */
  677       public int getYDpi() {
  678           return Math.round(yDpi * INCH_IN_METER_UNIT);
  679       }
  680   
  681       /**
  682        * Set the DPI resolution.
  683        *
  684        * @param xDpi  The number of dots per inch for the X axis.
  685        * @param yDpi  The number of dots per inch for the Y axis.
  686        */
  687       public void setDpi(int xDpi, int yDpi) {
  688           this.xDpi = Math.round(xDpi / INCH_IN_METER_UNIT);
  689           this.yDpi = Math.round(yDpi / INCH_IN_METER_UNIT);
  690       }
  691   
  692       /**
  693        * Write a PNG "pHYs" chunk into the pngBytes array.
  694        */
  695       protected void writeResolution() {
  696           if (xDpi > 0 && yDpi > 0) {
  697   
  698               final int startPos = bytePos = writeInt4(9, bytePos);
  699               bytePos = writeBytes(PHYS, bytePos);
  700               bytePos = writeInt4(xDpi, bytePos);
  701               bytePos = writeInt4(yDpi, bytePos);
  702               bytePos = writeByte(1, bytePos); // unit is the meter.
  703   
  704               crc.reset();
  705               crc.update(pngBytes, startPos, bytePos - startPos);
  706               crcValue = crc.getValue();
  707               bytePos = writeInt4((int) crcValue, bytePos);
  708           }
  709       }
  710   }

Save This Page
Home » jcommon-1.0.13 » com.keypoint » [javadoc | source]