예제 #1
0
 /**
  * Writes the data for this segment to the stream in valid JPEG format. The length written takes
  * the thumbnail width and height into account. If necessary, the thumbnail is clipped to 255 x
  * 255 and a warning is sent to the writer argument. Progress updates are sent to the writer
  * argument.
  */
 void write(ImageOutputStream ios, BufferedImage thumb, JPEGImageWriter writer)
     throws IOException {
   int thumbWidth = 0;
   int thumbHeight = 0;
   int thumbLength = 0;
   int[] thumbData = null;
   if (thumb != null) {
     // Clip if necessary and get the data in thumbData
     thumbWidth = thumb.getWidth();
     thumbHeight = thumb.getHeight();
     if ((thumbWidth > MAX_THUMB_WIDTH) || (thumbHeight > MAX_THUMB_HEIGHT)) {
       writer.warningOccurred(JPEGImageWriter.WARNING_THUMB_CLIPPED);
     }
     thumbWidth = Math.min(thumbWidth, MAX_THUMB_WIDTH);
     thumbHeight = Math.min(thumbHeight, MAX_THUMB_HEIGHT);
     thumbData = thumb.getRaster().getPixels(0, 0, thumbWidth, thumbHeight, (int[]) null);
     thumbLength = thumbData.length;
   }
   length = DATA_SIZE + LENGTH_SIZE + thumbLength;
   writeTag(ios);
   byte[] id = {0x4A, 0x46, 0x49, 0x46, 0x00};
   ios.write(id);
   ios.write(majorVersion);
   ios.write(minorVersion);
   ios.write(resUnits);
   write2bytes(ios, Xdensity);
   write2bytes(ios, Ydensity);
   ios.write(thumbWidth);
   ios.write(thumbHeight);
   if (thumbData != null) {
     writer.thumbnailStarted(0);
     writeThumbnailData(ios, thumbData, writer);
     writer.thumbnailComplete();
   }
 }
예제 #2
0
  private void writeGraphicControlExtension(
      int disposalMethod,
      boolean userInputFlag,
      boolean transparentColorFlag,
      int delayTime,
      int transparentColorIndex)
      throws IOException {
    try {
      stream.write(0x21);
      stream.write(0xf9);

      stream.write(4);

      int packedFields = (disposalMethod & 0x3) << 2;
      if (userInputFlag) {
        packedFields |= 0x2;
      }
      if (transparentColorFlag) {
        packedFields |= 0x1;
      }
      stream.write(packedFields);

      stream.writeShort((short) delayTime);

      stream.write(transparentColorIndex);
      stream.write(0x00);
    } catch (IOException e) {
      throw new IIOException("I/O error writing Graphic Control Extension!", e);
    }
  }
예제 #3
0
 void write(ImageOutputStream ios, JPEGImageWriter writer) throws IOException {
   length = LENGTH_SIZE + DATA_SIZE + thumb.getLength();
   writeTag(ios);
   byte[] id = {0x4A, 0x46, 0x58, 0x58, 0x00};
   ios.write(id);
   ios.write(code);
   thumb.write(ios, writer);
 }
예제 #4
0
 void write(ImageOutputStream ios, JPEGImageWriter writer) throws IOException {
   if ((thumbWidth > MAX_THUMB_WIDTH) || (thumbHeight > MAX_THUMB_HEIGHT)) {
     writer.warningOccurred(JPEGImageWriter.WARNING_THUMB_CLIPPED);
   }
   thumbWidth = Math.min(thumbWidth, MAX_THUMB_WIDTH);
   thumbHeight = Math.min(thumbHeight, MAX_THUMB_HEIGHT);
   ios.write(thumbWidth);
   ios.write(thumbHeight);
 }
예제 #5
0
  public void writeBits(int bits, int numbits) throws IOException {
    int bitsWritten = 0;
    int numBytes = 255; // gif block count
    do {
      // This handles the GIF block count stuff
      if ((index_ == 254 && bitsLeft_ == 0) || index_ > 254) {
        if (blocks_) output_.write(numBytes);

        output_.write(buffer_, 0, numBytes);

        buffer_[0] = 0;
        index_ = 0;
        bitsLeft_ = 8;
      }

      if (numbits <= bitsLeft_) // bits contents fit in current index byte
      {
        if (blocks_) // GIF
        {
          buffer_[index_] |= (bits & ((1 << numbits) - 1)) << (8 - bitsLeft_);
          bitsWritten += numbits;
          bitsLeft_ -= numbits;
          numbits = 0;
        } else {
          buffer_[index_] |= (bits & ((1 << numbits) - 1)) << (bitsLeft_ - numbits);
          bitsWritten += numbits;
          bitsLeft_ -= numbits;
          numbits = 0;
        }
      } else // bits overflow from current byte to next.
      {
        if (blocks_) // GIF
        {
          // if bits  > space left in current byte then the lowest order bits
          // of code are taken and put in current byte and rest put in next.
          buffer_[index_] |= (bits & ((1 << bitsLeft_) - 1)) << (8 - bitsLeft_);
          bitsWritten += bitsLeft_;
          bits >>= bitsLeft_;
          numbits -= bitsLeft_;
          buffer_[++index_] = 0;
          bitsLeft_ = 8;
        } else {
          // if bits  > space left in current byte then the highest order bits
          // of code are taken and put in current byte and rest put in next.
          // at highest order bit location !!
          int topbits = (bits >>> (numbits - bitsLeft_)) & ((1 << bitsLeft_) - 1);
          buffer_[index_] |= topbits;
          numbits -= bitsLeft_; // ok this many bits gone off the top
          bitsWritten += bitsLeft_;
          buffer_[++index_] = 0; // next index
          bitsLeft_ = 8;
        }
      }

    } while (numbits != 0);
  }
예제 #6
0
 public void flush() throws IOException {
   int numBytes = index_ + (bitsLeft_ == 8 ? 0 : 1);
   if (numBytes > 0) {
     if (blocks_) output_.write(numBytes);
     output_.write(buffer_, 0, numBytes);
     buffer_[0] = 0;
     index_ = 0;
     bitsLeft_ = 8;
   }
 }
예제 #7
0
 private void writeBlocks(byte[] data) throws IOException {
   if (data != null && data.length > 0) {
     int offset = 0;
     while (offset < data.length) {
       int len = Math.min(data.length - offset, 255);
       stream.write(len);
       stream.write(data, offset, len);
       offset += len;
     }
   }
 }
예제 #8
0
 private void writeCommentExtension(GIFWritableImageMetadata im) throws IOException {
   if (im.comments != null) {
     try {
       Iterator iter = im.comments.iterator();
       while (iter.hasNext()) {
         stream.write(0x21);
         stream.write(0xfe);
         writeBlocks((byte[]) iter.next());
         stream.write(0x00);
       }
     } catch (IOException e) {
       throw new IIOException("I/O error writing Comment Extension!", e);
     }
   }
 }
  /**
   * This method writes the Contiguous codestream box
   *
   * @param cs The contiguous codestream
   * @exception java.io.IOException If an I/O error ocurred.
   */
  public void writeContiguousCodeStreamBox() throws IOException {

    // when write a jp2 file
    if (metadata != null) {
      // Write box length (LBox)
      // This value is set to 0 since in this implementation, this box is
      // always last
      stream.writeInt(clength + 8);

      // Write contiguous codestream box name (TBox)
      stream.writeInt(CONTIGUOUS_CODESTREAM_BOX);
    }
    // Read and buffer the codestream
    BEBufferedRandomAccessFile fi = new BEBufferedRandomAccessFile(file, "rw+");
    int remainder = clength;
    byte[] codestream = new byte[1024];

    while (remainder > 0) {
      int len = remainder > 1024 ? 1024 : remainder;
      fi.readFully(codestream, 0, len);

      // Write codestream
      stream.write(codestream, 0, len);
      remainder -= len;
    }

    // Close the file.
    fi.close();
  }
예제 #10
0
  private void writeHeader(
      String version,
      int logicalScreenWidth,
      int logicalScreenHeight,
      int colorResolution,
      int pixelAspectRatio,
      int backgroundColorIndex,
      boolean sortFlag,
      int bitsPerPixel,
      byte[] globalColorTable)
      throws IOException {
    try {
      // Signature
      stream.writeBytes("GIF" + version);

      // Screen Descriptor
      // Width
      stream.writeShort((short) logicalScreenWidth);

      // Height
      stream.writeShort((short) logicalScreenHeight);

      // Global Color Table
      // Packed fields
      int packedFields = globalColorTable != null ? 0x80 : 0x00;
      packedFields |= ((colorResolution - 1) & 0x7) << 4;
      if (sortFlag) {
        packedFields |= 0x8;
      }
      packedFields |= (bitsPerPixel - 1);
      stream.write(packedFields);

      // Background color index
      stream.write(backgroundColorIndex);

      // Pixel aspect ratio
      stream.write(pixelAspectRatio);

      // Global Color Table
      if (globalColorTable != null) {
        stream.write(globalColorTable);
      }
    } catch (IOException e) {
      throw new IIOException("I/O error writing header!", e);
    }
  }
예제 #11
0
  /**
   * Write out the given profile to the stream, embedded in the necessary number of APP2 segments,
   * per the ICC spec. This is the only mechanism for writing an ICC profile to a stream.
   */
  static void writeICC(ICC_Profile profile, ImageOutputStream ios) throws IOException {
    int LENGTH_LENGTH = 2;
    final String ID = "ICC_PROFILE";
    int ID_LENGTH = ID.length() + 1; // spec says it's null-terminated
    int COUNTS_LENGTH = 2;
    int MAX_ICC_CHUNK_SIZE = 65535 - LENGTH_LENGTH - ID_LENGTH - COUNTS_LENGTH;

    byte[] data = profile.getData();
    int numChunks = data.length / MAX_ICC_CHUNK_SIZE;
    if ((data.length % MAX_ICC_CHUNK_SIZE) != 0) {
      numChunks++;
    }
    int chunkNum = 1;
    int offset = 0;
    for (int i = 0; i < numChunks; i++) {
      int dataLength = Math.min(data.length - offset, MAX_ICC_CHUNK_SIZE);
      int segLength = dataLength + COUNTS_LENGTH + ID_LENGTH + LENGTH_LENGTH;
      ios.write(0xff);
      ios.write(JPEG.APP2);
      MarkerSegment.write2bytes(ios, segLength);
      byte[] id = ID.getBytes("US-ASCII");
      ios.write(id);
      ios.write(0); // Null-terminate the string
      ios.write(chunkNum++);
      ios.write(numChunks);
      ios.write(data, offset, dataLength);
      offset += dataLength;
    }
  }
예제 #12
0
  private void writePlainTextExtension(GIFWritableImageMetadata im) throws IOException {
    if (im.hasPlainTextExtension) {
      try {
        stream.write(0x21);
        stream.write(0x1);

        stream.write(12);

        stream.writeShort(im.textGridLeft);
        stream.writeShort(im.textGridTop);
        stream.writeShort(im.textGridWidth);
        stream.writeShort(im.textGridHeight);
        stream.write(im.characterCellWidth);
        stream.write(im.characterCellHeight);
        stream.write(im.textForegroundColor);
        stream.write(im.textBackgroundColor);

        writeBlocks(im.text);

        stream.write(0x00);
      } catch (IOException e) {
        throw new IIOException("I/O error writing Plain Text Extension!", e);
      }
    }
  }
 private void writeBox(IIOMetadataNode node) throws IOException {
   int type = Box.getTypeInt((String) Box.getAttribute(node, "Type"));
   int length = new Integer((String) Box.getAttribute(node, "Length")).intValue();
   Box box = Box.createBox(type, node);
   otherLength += length;
   stream.writeInt(length);
   stream.writeInt(type);
   byte[] data = box.getContent();
   stream.write(data, 0, data.length);
 }
예제 #14
0
 /*
  * Write out the values in the integer array as a sequence of bytes,
  * reporting progress to the writer argument.
  */
 void writeThumbnailData(ImageOutputStream ios, int[] thumbData, JPEGImageWriter writer)
     throws IOException {
   int progInterval = thumbData.length / 20; // approx. every 5%
   if (progInterval == 0) {
     progInterval = 1;
   }
   for (int i = 0; i < thumbData.length; i++) {
     ios.write(thumbData[i]);
     if ((i > progInterval) && (i % progInterval == 0)) {
       writer.thumbnailProgress(((float) i * 100) / ((float) thumbData.length));
     }
   }
 }
예제 #15
0
  public void write(IIOMetadata streamMetadata, IIOImage img, ImageWriteParam param)
      throws IOException {
    ImageOutputStream out = (ImageOutputStream) getOutput();

    if (!(img.getRenderedImage() instanceof BufferedImage)) {
      throw new IOException(getClass().getName() + "write:\nCan only write BufferedImage objects");
    }
    BufferedImage image = (BufferedImage) img.getRenderedImage();

    int width = image.getWidth();
    int height = image.getHeight();

    try {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      JFIFOutputStream os;

      if (image.getType() == BufferedImage.TYPE_BYTE_GRAY) { // one   component;  grey scale
        os = new JFIFOutputStream(baos, false, height, width); // SOF:start of frame

        WritableRaster raster = image.getRaster();
        DataBufferByte buffer = (DataBufferByte) raster.getDataBuffer();
        byte[] imgdata = (byte[]) buffer.getData();

        os.write(imgdata);
        os.close(); // EOF: end of frame
      } else if (image.getType() == BufferedImage.TYPE_INT_RGB) { // three components; YCbCr
        os = new JFIFOutputStream(baos, true, height, width); // SOF:start of frame

        WritableRaster raster = image.getRaster();
        DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer();
        int[] imgdata = (int[]) buffer.getData();

        os.write(imgdata);
        os.close(); // EOF: end of frame
      } else { // three components; YCbCr
        os = new JFIFOutputStream(baos, true, height, width); // SOF:start of frame
        for (int y = 0; y < height; y++) {
          for (int x = 0; x < width; x++) {
            os.write(image.getRGB(x, y));
          }
        }
        os.close(); // EOF: end of frame
      }
      out.write(baos.toByteArray()); // write to image stream
    } catch (Exception e) {
      e.printStackTrace();
      throw new IOException(
          getClass().getName() + ".write:\n\tCould not write image due to :\n\t" + e.getMessage());
    }
  }
예제 #16
0
  private void writeApplicationExtension(GIFWritableImageMetadata im) throws IOException {
    if (im.applicationIDs != null) {
      Iterator iterIDs = im.applicationIDs.iterator();
      Iterator iterCodes = im.authenticationCodes.iterator();
      Iterator iterData = im.applicationData.iterator();

      while (iterIDs.hasNext()) {
        try {
          stream.write(0x21);
          stream.write(0xff);

          stream.write(11);
          stream.write((byte[]) iterIDs.next(), 0, 8);
          stream.write((byte[]) iterCodes.next(), 0, 3);

          writeBlocks((byte[]) iterData.next());

          stream.write(0x00);
        } catch (IOException e) {
          throw new IIOException("I/O error writing Application Extension!", e);
        }
      }
    }
  }
예제 #17
0
  private void writeImageDescriptor(
      int imageLeftPosition,
      int imageTopPosition,
      int imageWidth,
      int imageHeight,
      boolean interlaceFlag,
      boolean sortFlag,
      int bitsPerPixel,
      byte[] localColorTable)
      throws IOException {

    try {
      stream.write(0x2c);

      stream.writeShort((short) imageLeftPosition);
      stream.writeShort((short) imageTopPosition);
      stream.writeShort((short) imageWidth);
      stream.writeShort((short) imageHeight);

      int packedFields = localColorTable != null ? 0x80 : 0x00;
      if (interlaceFlag) {
        packedFields |= 0x40;
      }
      if (sortFlag) {
        packedFields |= 0x8;
      }
      packedFields |= (bitsPerPixel - 1);
      stream.write(packedFields);

      if (localColorTable != null) {
        stream.write(localColorTable);
      }
    } catch (IOException e) {
      throw new IIOException("I/O error writing Image Descriptor!", e);
    }
  }
예제 #18
0
 void write(ImageOutputStream ios, JPEGImageWriter writer) throws IOException {
   int progInterval = data.length / 20; // approx. every 5%
   if (progInterval == 0) {
     progInterval = 1;
   }
   for (int offset = 0; offset < data.length; ) {
     int len = Math.min(progInterval, data.length - offset);
     ios.write(data, offset, len);
     offset += progInterval;
     float percentDone = ((float) offset * 100) / data.length;
     if (percentDone > 100.0F) {
       percentDone = 100.0F;
     }
     writer.thumbnailProgress(percentDone);
   }
 }
예제 #19
0
  /**
   * The image encoder.
   *
   * @param o - the image output stream
   * @param streamMetadata - metadata associated with this stream, or null
   * @param image - an IIOImage containing image data.
   * @param param - image writing parameters, or null
   * @exception IOException if a write error occurs
   */
  public void encode(
      ImageOutputStream o, IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param)
      throws IOException {
    int size;
    int value;
    int j;
    int rowCount;
    int rowIndex;
    int lastRowIndex;
    int[] bitmap;
    byte rgb[] = new byte[3];
    size = (infoHeader.biWidth * infoHeader.biHeight) - 1;
    rowCount = 1;
    rowIndex = size - infoHeader.biWidth;
    lastRowIndex = rowIndex;
    try {
      bitmap = new int[infoHeader.biWidth * infoHeader.biHeight];
      PixelGrabber pg =
          new PixelGrabber(
              (BufferedImage) image.getRenderedImage(),
              0,
              0,
              infoHeader.biWidth,
              infoHeader.biHeight,
              bitmap,
              0,
              infoHeader.biWidth);
      pg.grabPixels();

      for (j = 0; j < size; j++) {
        value = bitmap[rowIndex];

        rgb[0] = (byte) (value & 0xFF);
        rgb[1] = (byte) ((value >> 8) & 0xFF);
        rgb[2] = (byte) ((value >> 16) & 0xFF);
        o.write(rgb);
        if (rowCount == infoHeader.biWidth) {
          rowCount = 1;
          rowIndex = lastRowIndex - infoHeader.biWidth;
          lastRowIndex = rowIndex;
        } else rowCount++;
        rowIndex++;
      }
    } catch (Exception wb) {
      wb.printStackTrace();
    }
  }
예제 #20
0
 void write(ImageOutputStream ios, JPEGImageWriter writer) throws IOException {
   super.write(ios, writer); // width and height
   // Write the palette (must be 768 bytes)
   byte[] palette = new byte[768];
   IndexColorModel icm = (IndexColorModel) thumbnail.getColorModel();
   byte[] reds = new byte[256];
   byte[] greens = new byte[256];
   byte[] blues = new byte[256];
   icm.getReds(reds);
   icm.getGreens(greens);
   icm.getBlues(blues);
   for (int i = 0; i < 256; i++) {
     palette[i * 3] = reds[i];
     palette[i * 3 + 1] = greens[i];
     palette[i * 3 + 2] = blues[i];
   }
   ios.write(palette);
   writePixels(ios, writer);
 }
예제 #21
0
  private IFD writeRGBImage(
      ImageOutputStream out, BufferedImage image, int comp, TIFFImageWriteParam param)
      throws IOException {
    image = convert(image, BufferedImage.TYPE_INT_RGB);
    try {
      int width = image.getWidth();
      int height = image.getHeight();

      IFD ifd = new IFD(); // entries need to be in tag order !

      ifd.add(new DEFactory.NewSubfileTypeDE(2)); // 254 single page of multipage file
      ifd.add(new DEFactory.ImageWidthDE(width)); // 256
      ifd.add(new DEFactory.ImageLengthDE(height)); // 257

      DEFactory.BitsPerSampleDE bpss = new DEFactory.BitsPerSampleDE(3);
      bpss.setBitsPerSample(0, 8); // red
      bpss.setBitsPerSample(1, 8); // green
      bpss.setBitsPerSample(2, 8); // blue
      ifd.add(bpss); // 258

      ifd.add(new DEFactory.CompressionDE(comp)); // 259
      ifd.add(new DEFactory.PhotometricInterpretationDE(RGB)); // 262

      int maxrps, maxstripes; // max RowsPerStrip
      if ((1 << 13) <= width) {
        maxrps = 1;
        maxstripes = height; // one row per strip
      } else {
        maxrps = (1 << 13) / width;
        maxstripes = (height + maxrps - 1) / maxrps;
      }
      if (comp == JPEG) {
        maxrps = ((maxrps + 8 - 1) / 8) * 8;
        maxstripes = (height + maxrps - 1) / maxrps;
      }
      DEFactory.StripOffsetsDE offsets = new DEFactory.StripOffsetsDE(maxstripes);
      ifd.add(offsets); // 273
      ifd.add(new DEFactory.SamplesPerPixelDE(3)); // 277
      ifd.add(new DEFactory.RowsPerStripDE(maxrps)); // 278
      DEFactory.StripByteCountsDE counts = new DEFactory.StripByteCountsDE(maxstripes);
      ifd.add(counts); // 279
      if (param == null) {
        ifd.add(new DEFactory.XResolutionDE(72.0)); // 282
        ifd.add(new DEFactory.YResolutionDE(72.0)); // 283
      } else {
        ifd.add(new DEFactory.XResolutionDE(param.getXResolution())); // 282
        ifd.add(new DEFactory.YResolutionDE(param.getYResolution())); // 283
      }
      ifd.add(new DEFactory.ResolutionUnitDE(Inch)); // 296

      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      OutputStream os = baos;
      JPEGOutputStream jpegos = null;

      if (comp == JPEG) { // add JPEGTables tag
        jpegos = new JPEGOutputStream(baos);

        int quality = (param == null) ? 50 : (int) (param.getCompressionQuality() * 100);

        jpegos.setZZQuantizationTable(0, JPEGConstants.LQT, quality);
        jpegos.setRawDCHuffmanTable(0, JPEGConstants.HLDCTable);
        jpegos.setRawACHuffmanTable(0, JPEGConstants.HLACTable);

        jpegos.defineQuantizationTables();
        jpegos.defineHuffmanTables();
        jpegos.close();

        DEFactory.JPEGTablesDE jpegtables = new DEFactory.JPEGTablesDE(baos.toByteArray());
        ifd.add(jpegtables); // 347

        baos.reset();

        os = jpegos;
      }

      WritableRaster raster = image.getRaster();
      DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer();
      int[] imgdata = (int[]) buffer.getData();

      int index = 0;
      for (int y = 0; y < height; y += maxrps) {
        /*
        Assume rgb image.

        Each strip: evaluate r g b colour
                    save in byte array
                    write to image file
        */

        if ((height - y) < maxrps) {
          maxrps = height - y;
        }

        if (jpegos != null) { // jpeg: SOI,SOF,SOS marker
          jpegos.startOfImage();
          int[] hv = {0x11, 0x11, 0x11}; // (Hi<<4)|Vi
          int[] q = {0, 0, 0}; // quantization table 0
          jpegos.startOfFrame(maxrps, width, hv, q);
          int[] sel = {0, 0, 0}; // DC,AC code table 0
          jpegos.startOfScan(sel);
        }

        for (int i = 0; i < maxrps; i++) { // write RGB data
          for (int x = 0; x < width; x++) {
            int c = imgdata[x + (y + i) * width];
            os.write((c >> 16) & 0x000000FF);
            os.write((c >> 8) & 0x000000FF);
            os.write(c & 0x000000FF);
          }
        }
        os.close(); // jpeg: EOI marker

        byte[] data = baos.toByteArray();
        counts.setCount(index, data.length); // update ifd strip counter array
        offsets.setOffset(index, out.getStreamPosition()); // update ifd image data offset array
        out.write(data); // write to image stream
        baos.reset();
        index++;
      }
      return ifd;
    } catch (Exception e) {
      e.printStackTrace();
      throw new IOException(getClass().getName() + ".writeRGBImage:\n\t" + e.getMessage());
    }
  }
예제 #22
0
  private void writeRasterData(
      RenderedImage image,
      Rectangle sourceBounds,
      Dimension destSize,
      ImageWriteParam param,
      boolean interlaceFlag)
      throws IOException {

    int sourceXOffset = sourceBounds.x;
    int sourceYOffset = sourceBounds.y;
    int sourceWidth = sourceBounds.width;
    int sourceHeight = sourceBounds.height;

    int destWidth = destSize.width;
    int destHeight = destSize.height;

    int periodX;
    int periodY;
    if (param == null) {
      periodX = 1;
      periodY = 1;
    } else {
      periodX = param.getSourceXSubsampling();
      periodY = param.getSourceYSubsampling();
    }

    SampleModel sampleModel = image.getSampleModel();
    int bitsPerPixel = sampleModel.getSampleSize()[0];

    int initCodeSize = bitsPerPixel;
    if (initCodeSize == 1) {
      initCodeSize++;
    }
    stream.write(initCodeSize);

    LZWCompressor compressor = new LZWCompressor(stream, initCodeSize, false);

    boolean isOptimizedCase =
        periodX == 1
            && periodY == 1
            && sampleModel instanceof ComponentSampleModel
            && image.getNumXTiles() == 1
            && image.getNumYTiles() == 1
            && image.getTile(0, 0).getDataBuffer() instanceof DataBufferByte;

    int numRowsWritten = 0;

    int progressReportRowPeriod = Math.max(destHeight / 20, 1);

    processImageStarted(imageIndex);

    if (interlaceFlag) {
      if (DEBUG) System.out.println("Writing interlaced");

      if (isOptimizedCase) {
        Raster tile = image.getTile(0, 0);
        byte[] data = ((DataBufferByte) tile.getDataBuffer()).getData();
        ComponentSampleModel csm = (ComponentSampleModel) tile.getSampleModel();
        int offset =
            csm.getOffset(
                sourceXOffset - tile.getSampleModelTranslateX(),
                sourceYOffset - tile.getSampleModelTranslateY(),
                0);
        int lineStride = csm.getScanlineStride();

        writeRowsOpt(
            data,
            offset,
            lineStride,
            compressor,
            0,
            8,
            destWidth,
            destHeight,
            numRowsWritten,
            progressReportRowPeriod);

        if (abortRequested()) {
          return;
        }

        numRowsWritten += destHeight / 8;

        writeRowsOpt(
            data,
            offset,
            lineStride,
            compressor,
            4,
            8,
            destWidth,
            destHeight,
            numRowsWritten,
            progressReportRowPeriod);

        if (abortRequested()) {
          return;
        }

        numRowsWritten += (destHeight - 4) / 8;

        writeRowsOpt(
            data,
            offset,
            lineStride,
            compressor,
            2,
            4,
            destWidth,
            destHeight,
            numRowsWritten,
            progressReportRowPeriod);

        if (abortRequested()) {
          return;
        }

        numRowsWritten += (destHeight - 2) / 4;

        writeRowsOpt(
            data,
            offset,
            lineStride,
            compressor,
            1,
            2,
            destWidth,
            destHeight,
            numRowsWritten,
            progressReportRowPeriod);
      } else {
        writeRows(
            image,
            compressor,
            sourceXOffset,
            periodX,
            sourceYOffset,
            8 * periodY,
            sourceWidth,
            0,
            8,
            destWidth,
            destHeight,
            numRowsWritten,
            progressReportRowPeriod);

        if (abortRequested()) {
          return;
        }

        numRowsWritten += destHeight / 8;

        writeRows(
            image,
            compressor,
            sourceXOffset,
            periodX,
            sourceYOffset + 4 * periodY,
            8 * periodY,
            sourceWidth,
            4,
            8,
            destWidth,
            destHeight,
            numRowsWritten,
            progressReportRowPeriod);

        if (abortRequested()) {
          return;
        }

        numRowsWritten += (destHeight - 4) / 8;

        writeRows(
            image,
            compressor,
            sourceXOffset,
            periodX,
            sourceYOffset + 2 * periodY,
            4 * periodY,
            sourceWidth,
            2,
            4,
            destWidth,
            destHeight,
            numRowsWritten,
            progressReportRowPeriod);

        if (abortRequested()) {
          return;
        }

        numRowsWritten += (destHeight - 2) / 4;

        writeRows(
            image,
            compressor,
            sourceXOffset,
            periodX,
            sourceYOffset + periodY,
            2 * periodY,
            sourceWidth,
            1,
            2,
            destWidth,
            destHeight,
            numRowsWritten,
            progressReportRowPeriod);
      }
    } else {
      if (DEBUG) System.out.println("Writing non-interlaced");

      if (isOptimizedCase) {
        Raster tile = image.getTile(0, 0);
        byte[] data = ((DataBufferByte) tile.getDataBuffer()).getData();
        ComponentSampleModel csm = (ComponentSampleModel) tile.getSampleModel();
        int offset =
            csm.getOffset(
                sourceXOffset - tile.getSampleModelTranslateX(),
                sourceYOffset - tile.getSampleModelTranslateY(),
                0);
        int lineStride = csm.getScanlineStride();

        writeRowsOpt(
            data,
            offset,
            lineStride,
            compressor,
            0,
            1,
            destWidth,
            destHeight,
            numRowsWritten,
            progressReportRowPeriod);
      } else {
        writeRows(
            image,
            compressor,
            sourceXOffset,
            periodX,
            sourceYOffset,
            periodY,
            sourceWidth,
            0,
            1,
            destWidth,
            destHeight,
            numRowsWritten,
            progressReportRowPeriod);
      }
    }

    if (abortRequested()) {
      return;
    }

    processImageProgress(100.0F);

    compressor.flush();

    stream.write(0x00);

    processImageComplete();
  }
  /**
   * Writes raster data from the given in-memory source buffer into the data sink specified by the
   * given source band and region.
   *
   * <p>
   *
   * <h3>Source band</h3>
   *
   * The source band is used to identify the data sink in which this method transfers the sample
   * values given in the source buffer. The method does not modify the pixel data of the given
   * source band at all.
   *
   * <p>
   *
   * <h3>Source buffer</h3>
   *
   * The first element of the source buffer corresponds to the given <code>sourceOffsetX</code> and
   * <code>sourceOffsetY</code> of the source region. These parameters are an offset within the
   * band's raster data and <b>not</b> an offset within the source buffer.<br>
   * The number of elements in the buffer must be exactly be <code>sourceWidth * sourceHeight</code>
   * . The pixel values to be writte are considered to be stored in line-by-line order, so the
   * raster X co-ordinate varies faster than the Y.
   *
   * <p>
   *
   * <h3>Source region</h3>
   *
   * The given destination region specified by the <code>sourceOffsetX</code>, <code>sourceOffsetY
   * </code>, <code>sourceWidth</code> and <code>sourceHeight</code> parameters is given in the
   * source band's raster co-ordinates. These co-ordinates are identical with the destination raster
   * co-ordinates since product writers do not support spectral or spatial subsets.
   *
   * @param sourceBand the source band which identifies the data sink to which to write the sample
   *     values
   * @param regionData the data buffer which provides the sample values to be written
   * @param regionX the X-offset in the band's raster co-ordinates
   * @param regionY the Y-offset in the band's raster co-ordinates
   * @param regionWidth the width of region to be written given in the band's raster co-ordinates
   * @param regionHeight the height of region to be written given in the band's raster co-ordinates
   * @throws java.io.IOException if an I/O error occurs
   * @throws IllegalArgumentException if the number of elements source buffer not equals <code>
   *     sourceWidth *
   *                                  sourceHeight</code> or the source region is out of the band's
   *     raster
   * @see Band#getRasterWidth()
   * @see Band#getRasterHeight()
   */
  public void writeBandRasterData(
      final Band sourceBand,
      final int regionX,
      final int regionY,
      final int regionWidth,
      final int regionHeight,
      final ProductData regionData,
      ProgressMonitor pm)
      throws IOException {
    if (!tempProduct.containsBand(sourceBand.getName())) {
      throw new IllegalArgumentException(
          "'" + sourceBand.getName() + "' is not a band of the product");
    }
    final int bandDataType = ifd.getBandDataType();
    final int stripIndex = getStripIndex(sourceBand);
    final TiffValue[] offsetValues = ifd.getEntry(TiffTag.STRIP_OFFSETS).getValues();
    final long stripOffset = ((TiffLong) offsetValues[stripIndex]).getValue();
    final TiffValue[] bitsPerSampleValues = ifd.getEntry(TiffTag.BITS_PER_SAMPLE).getValues();
    final long elemSize = ((TiffShort) bitsPerSampleValues[stripIndex]).getValue() / 8;
    final long sourceWidthBytes = sourceBand.getSceneRasterWidth() * elemSize;
    final long regionOffsetXInBytes = regionX * elemSize;
    final long pixelOffset = sourceWidthBytes * regionY + regionOffsetXInBytes;
    final long startOffset = stripOffset + pixelOffset;

    pm.beginTask("Writing band '" + sourceBand.getName() + "'...", regionHeight);
    try {
      for (int y = 0; y < regionHeight; y++) {
        ios.seek(startOffset + y * sourceWidthBytes);
        final int stride = y * regionWidth;
        if (bandDataType == ProductData.TYPE_UINT8) {
          final byte[] data = new byte[regionWidth];
          for (int x = 0; x < regionWidth; x++) {
            data[x] = (byte) regionData.getElemUIntAt(stride + x);
          }
          ios.write(data);
        } else if (bandDataType == ProductData.TYPE_INT8) {
          final byte[] data = new byte[regionWidth];
          for (int x = 0; x < regionWidth; x++) {
            data[x] = (byte) regionData.getElemIntAt(stride + x);
          }
          ios.write(data);
        } else if (bandDataType == ProductData.TYPE_UINT16) {
          final short[] data = new short[regionWidth];
          for (int x = 0; x < regionWidth; x++) {
            data[x] = (short) regionData.getElemUIntAt(stride + x);
          }
          ios.writeShorts(data, 0, regionWidth);
        } else if (bandDataType == ProductData.TYPE_INT16) {
          final short[] data = new short[regionWidth];
          for (int x = 0; x < regionWidth; x++) {
            data[x] = (short) regionData.getElemIntAt(stride + x);
          }
          ios.writeShorts(data, 0, regionWidth);
        } else if (bandDataType == ProductData.TYPE_UINT32) {
          final int[] data = new int[regionWidth];
          for (int x = 0; x < regionWidth; x++) {
            data[x] = (int) regionData.getElemUIntAt(stride + x);
          }
          ios.writeInts(data, 0, regionWidth);
        } else if (bandDataType == ProductData.TYPE_INT32) {
          final int[] data = new int[regionWidth];
          for (int x = 0; x < regionWidth; x++) {
            data[x] = regionData.getElemIntAt(stride + x);
          }
          ios.writeInts(data, 0, regionWidth);
        } else if (bandDataType == ProductData.TYPE_FLOAT32) {
          final float[] data = new float[regionWidth];
          for (int x = 0; x < regionWidth; x++) {
            data[x] = regionData.getElemFloatAt(stride + x);
          }
          ios.writeFloats(data, 0, regionWidth);
        } else if (bandDataType == ProductData.TYPE_FLOAT64) {
          final double[] data = new double[regionWidth];
          for (int x = 0; x < regionWidth; x++) {
            data[x] = regionData.getElemDoubleAt(stride + x);
          }
          ios.writeDoubles(data, 0, regionWidth);
        }
        pm.worked(1);
      }
    } finally {
      pm.done();
    }
  }
예제 #24
0
  public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param)
      throws IOException {
    if (stream == null) {
      throw new IllegalStateException(I18N.getString("WBMPImageWriter3"));
    }

    if (image == null) {
      throw new IllegalArgumentException(I18N.getString("WBMPImageWriter4"));
    }

    clearAbortRequest();
    processImageStarted(0);
    if (param == null) param = getDefaultWriteParam();

    RenderedImage input = null;
    Raster inputRaster = null;
    boolean writeRaster = image.hasRaster();
    Rectangle sourceRegion = param.getSourceRegion();
    SampleModel sampleModel = null;

    if (writeRaster) {
      inputRaster = image.getRaster();
      sampleModel = inputRaster.getSampleModel();
    } else {
      input = image.getRenderedImage();
      sampleModel = input.getSampleModel();

      inputRaster = input.getData();
    }

    checkSampleModel(sampleModel);
    if (sourceRegion == null) sourceRegion = inputRaster.getBounds();
    else sourceRegion = sourceRegion.intersection(inputRaster.getBounds());

    if (sourceRegion.isEmpty()) throw new RuntimeException(I18N.getString("WBMPImageWriter1"));

    int scaleX = param.getSourceXSubsampling();
    int scaleY = param.getSourceYSubsampling();
    int xOffset = param.getSubsamplingXOffset();
    int yOffset = param.getSubsamplingYOffset();

    sourceRegion.translate(xOffset, yOffset);
    sourceRegion.width -= xOffset;
    sourceRegion.height -= yOffset;

    int minX = sourceRegion.x / scaleX;
    int minY = sourceRegion.y / scaleY;
    int w = (sourceRegion.width + scaleX - 1) / scaleX;
    int h = (sourceRegion.height + scaleY - 1) / scaleY;

    Rectangle destinationRegion = new Rectangle(minX, minY, w, h);
    sampleModel = sampleModel.createCompatibleSampleModel(w, h);

    SampleModel destSM = sampleModel;

    // If the data are not formatted nominally then reformat.
    if (sampleModel.getDataType() != DataBuffer.TYPE_BYTE
        || !(sampleModel instanceof MultiPixelPackedSampleModel)
        || ((MultiPixelPackedSampleModel) sampleModel).getDataBitOffset() != 0) {
      destSM = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, w, h, 1, w + 7 >> 3, 0);
    }

    if (!destinationRegion.equals(sourceRegion)) {
      if (scaleX == 1 && scaleY == 1)
        inputRaster =
            inputRaster.createChild(
                inputRaster.getMinX(), inputRaster.getMinY(), w, h, minX, minY, null);
      else {
        WritableRaster ras = Raster.createWritableRaster(destSM, new Point(minX, minY));

        byte[] data = ((DataBufferByte) ras.getDataBuffer()).getData();

        for (int j = minY, y = sourceRegion.y, k = 0; j < minY + h; j++, y += scaleY) {

          for (int i = 0, x = sourceRegion.x; i < w; i++, x += scaleX) {
            int v = inputRaster.getSample(x, y, 0);
            data[k + (i >> 3)] |= v << (7 - (i & 7));
          }
          k += w + 7 >> 3;
        }
        inputRaster = ras;
      }
    }

    // If the data are not formatted nominally then reformat.
    if (!destSM.equals(inputRaster.getSampleModel())) {
      WritableRaster raster =
          Raster.createWritableRaster(
              destSM, new Point(inputRaster.getMinX(), inputRaster.getMinY()));
      raster.setRect(inputRaster);
      inputRaster = raster;
    }

    // Check whether the image is white-is-zero.
    boolean isWhiteZero = false;
    if (!writeRaster && input.getColorModel() instanceof IndexColorModel) {
      IndexColorModel icm = (IndexColorModel) input.getColorModel();
      isWhiteZero = icm.getRed(0) > icm.getRed(1);
    }

    // Get the line stride, bytes per row, and data array.
    int lineStride = ((MultiPixelPackedSampleModel) destSM).getScanlineStride();
    int bytesPerRow = (w + 7) / 8;
    byte[] bdata = ((DataBufferByte) inputRaster.getDataBuffer()).getData();

    // Write WBMP header.
    stream.write(0); // TypeField
    stream.write(0); // FixHeaderField
    stream.write(intToMultiByte(w)); // width
    stream.write(intToMultiByte(h)); // height

    // Write the data.
    if (!isWhiteZero && lineStride == bytesPerRow) {
      // Write the entire image.
      stream.write(bdata, 0, h * bytesPerRow);
      processImageProgress(100.0F);
    } else {
      // Write the image row-by-row.
      int offset = 0;
      if (!isWhiteZero) {
        // Black-is-zero
        for (int row = 0; row < h; row++) {
          if (abortRequested()) break;
          stream.write(bdata, offset, bytesPerRow);
          offset += lineStride;
          processImageProgress(100.0F * row / h);
        }
      } else {
        // White-is-zero: need to invert data.
        byte[] inverted = new byte[bytesPerRow];
        for (int row = 0; row < h; row++) {
          if (abortRequested()) break;
          for (int col = 0; col < bytesPerRow; col++) {
            inverted[col] = (byte) (~(bdata[col + offset]));
          }
          stream.write(inverted, 0, bytesPerRow);
          offset += lineStride;
          processImageProgress(100.0F * row / h);
        }
      }
    }

    if (abortRequested()) processWriteAborted();
    else {
      processImageComplete();
      stream.flushBefore(stream.getStreamPosition());
    }
  }
예제 #25
0
 /*     */ void write(ImageOutputStream paramImageOutputStream) /*     */ throws IOException
       /*     */ {
   /* 182 */ this.length = (2 + (this.data != null ? this.data.length : 0));
   /* 183 */ writeTag(paramImageOutputStream);
   /* 184 */ if (this.data != null) /* 185 */ paramImageOutputStream.write(this.data);
   /*     */ }
예제 #26
0
  private IFD writeBModHufImage(
      ImageOutputStream out, BufferedImage image, TIFFImageWriteParam param) throws IOException {
    try {
      int width = image.getWidth();
      int height = image.getHeight();

      IFD ifd = new IFD(); // entries need to be in tag order !

      ifd.add(new DEFactory.NewSubfileTypeDE(2)); // 254 single page of multipage file
      ifd.add(new DEFactory.ImageWidthDE(width)); // 256
      ifd.add(new DEFactory.ImageLengthDE(height)); // 257
      ifd.add(new DEFactory.CompressionDE(CCITTGROUP3MODHUFFMAN)); // 259
      ifd.add(new DEFactory.PhotometricInterpretationDE(WhiteIsZero)); // 262

      int maxrps, maxstripes; // max RowsPerStrip
      if ((1 << 13) <= width) {
        maxrps = 1;
        maxstripes = height; // one row per stripe
      } else {
        maxrps = (1 << 13) / width;
        maxstripes = (height + maxrps - 1) / maxrps;
      }

      DEFactory.StripOffsetsDE offsets = new DEFactory.StripOffsetsDE(maxstripes);
      ifd.add(offsets); // 273
      ifd.add(new DEFactory.RowsPerStripDE(maxrps)); // 278
      DEFactory.StripByteCountsDE counts = new DEFactory.StripByteCountsDE(maxstripes);
      ifd.add(counts); // 279

      if (param == null) {
        ifd.add(new DEFactory.XResolutionDE(72.0)); // 282
        ifd.add(new DEFactory.YResolutionDE(72.0)); // 283
      } else {
        ifd.add(new DEFactory.XResolutionDE(param.getXResolution())); // 282
        ifd.add(new DEFactory.YResolutionDE(param.getYResolution())); // 283
      }
      ifd.add(new DEFactory.ResolutionUnitDE(Inch)); // 296

      int index = 0;
      for (int y = 0; y < height; y += maxrps) {
        /*
        Assume bilevel image (black/white[=-1])

        Each strip: count run length
                    encode into modified hufman codes
                    swap bits
                    save in byte array
                    write to image file
        */

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        BitSwapOutputStream bsos = new BitSwapOutputStream(baos);
        ModHuffmanOutputStream mhos = new ModHuffmanOutputStream(bsos);

        RLEOutputStream rlos =
            new RLEOutputStream(mhos, 3); // rgb = 3 bytes per sample code word (not needed here)

        for (int i = 0; i < maxrps; i++) {
          if ((y + i) == height) {
            break;
          } // last strip might have less rows
          rlos.setStartCodeWord(-1); // white run first
          for (int x = 0; x < width; x++) {
            rlos.write(image.getRGB(x, y + i));
          }
          rlos.flush(); // write padding after ever image row
        }
        rlos.close();

        byte[] data = baos.toByteArray();
        counts.setCount(index, data.length); // update ifd strip counter array
        offsets.setOffset(index, out.getStreamPosition()); // update ifd image data offset array
        out.write(data); // write to image stream

        index++;
      }
      return ifd;
    } catch (Exception e) {
      e.printStackTrace();
      throw new IOException(getClass().getName() + ".writeBModHufImage:\n\t" + e.getMessage());
    }
  }
예제 #27
0
 /*     */ void writeTag(ImageOutputStream paramImageOutputStream) /*     */ throws IOException
       /*     */ {
   /* 172 */ paramImageOutputStream.write(255);
   /* 173 */ paramImageOutputStream.write(this.tag);
   /* 174 */ write2bytes(paramImageOutputStream, this.length);
   /*     */ }
예제 #28
0
  private IFD writeCMYKImage(ImageOutputStream out, BufferedImage image, TIFFImageWriteParam param)
      throws IOException {
    try {
      int width = image.getWidth();
      int height = image.getHeight();

      IFD ifd = new IFD(); // entries need to be in tag order !

      ifd.add(new DEFactory.NewSubfileTypeDE(2)); // 254 single page of multipage file
      ifd.add(new DEFactory.ImageWidthDE(width)); // 256
      ifd.add(new DEFactory.ImageLengthDE(height)); // 257

      DEFactory.BitsPerSampleDE bpss = new DEFactory.BitsPerSampleDE(4);
      bpss.setBitsPerSample(0, 8); // cyan
      bpss.setBitsPerSample(1, 8); // magneta
      bpss.setBitsPerSample(2, 8); // yellow
      bpss.setBitsPerSample(3, 8); // key (black)
      ifd.add(bpss); // 258

      ifd.add(new DEFactory.CompressionDE(NOCOMPRESSION)); // 259
      ifd.add(new DEFactory.PhotometricInterpretationDE(CMYK)); // 262

      int maxrps, maxstripes; // max RowsPerStrip
      if ((1 << 13) <= width) {
        maxrps = 1;
        maxstripes = height; // one row per strip
      } else {
        maxrps = (1 << 13) / width;
        maxstripes = (height + maxrps - 1) / maxrps;
      }

      DEFactory.StripOffsetsDE offsets = new DEFactory.StripOffsetsDE(maxstripes);
      ifd.add(offsets); // 273
      ifd.add(new DEFactory.SamplesPerPixelDE(4)); // 277
      ifd.add(new DEFactory.RowsPerStripDE(maxrps)); // 278
      DEFactory.StripByteCountsDE counts = new DEFactory.StripByteCountsDE(maxstripes);
      ifd.add(counts); // 279

      if (param == null) {
        ifd.add(new DEFactory.XResolutionDE(72.0)); // 282
        ifd.add(new DEFactory.YResolutionDE(72.0)); // 283
      } else {
        ifd.add(new DEFactory.XResolutionDE(param.getXResolution())); // 282
        ifd.add(new DEFactory.YResolutionDE(param.getYResolution())); // 283
      }
      ifd.add(new DEFactory.ResolutionUnitDE(Inch)); // 296

      int index = 0;
      for (int y = 0; y < height; y += maxrps) {
        /*
        Assume rgb image.

        Each strip: evaluate c m y k colour
                    save in byte array
                    write to image file
        */

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        for (int i = 0; i < maxrps; i++) {
          if ((y + i) == height) {
            break;
          } // last strip might have less rows
          for (int x = 0; x < width; x++) {
            int c = image.getRGB(x, y + i);

            int R = (c >> 16) & 0x00FF;
            int G = (c >> 8) & 0x00FF;
            int B = (c) & 0x00FF;

            if ((R == 255) && (G == 255) && (B == 255)) {
              baos.write(0);
              baos.write(0);
              baos.write(0);
              baos.write(0);
            } else {
              double C = 1.0 - R / 255.0;
              double M = 1.0 - G / 255.0;
              double Y = 1.0 - B / 255.0;

              double K = C;
              if (M < K) {
                K = M;
              }
              if (Y < K) {
                K = Y;
              }

              C = ((C - K) / (1.0 - K)) * 255.0;
              M = ((M - K) / (1.0 - K)) * 255.0;
              Y = ((Y - K) / (1.0 - K)) * 255.0;
              K *= 255.0;

              baos.write((int) C);
              baos.write((int) M);
              baos.write((int) Y);
              baos.write((int) K);
            }
          }
        }
        baos.close();

        byte[] data = baos.toByteArray();
        counts.setCount(index, data.length); // update ifd strip counter array
        offsets.setOffset(index, out.getStreamPosition()); // update ifd image data offset array
        out.write(data); // write to image stream

        index++;
      }
      return ifd;
    } catch (Exception e) {
      e.printStackTrace();
      throw new IOException(getClass().getName() + ".writeCMYKImage:\n\t" + e.getMessage());
    }
  }
예제 #29
0
 /*     */ static void write2bytes(ImageOutputStream paramImageOutputStream, int paramInt)
     /*     */ throws IOException
       /*     */ {
   /* 191 */ paramImageOutputStream.write(paramInt >> 8 & 0xFF);
   /* 192 */ paramImageOutputStream.write(paramInt & 0xFF);
   /*     */ }
예제 #30
0
  private IFD writeYCbCrImage(
      ImageOutputStream out, BufferedImage image, int comp, TIFFImageWriteParam param)
      throws IOException {
    image = convert(image, BufferedImage.TYPE_INT_RGB);
    try {
      int width = image.getWidth();
      int height = image.getHeight();

      IFD ifd = new IFD(); // entries need to be in tag order !

      int ss = (param == null) ? 0x22 : param.getSubSampling();

      int ssh = (ss >> 4) & 0x0F;
      int ssv = ss & 0x0F;

      if (ssh
          < ssv) { // YCbCrSubsampleVert shall always be less than or equal to YCbCrSubsampleHoriz.
        throw new IOException(
            "Internal error: YCbCrSubsampleVert is not less than YCbCrSubsampleHoriz.");
      }

      //      int ww=((width +ssh-1)/ssh)*ssh;                                   // [1] p.92
      //      int hh=((height+ssv-1)/ssv)*ssv;
      int ww = width;
      int hh = height;

      ifd.add(new DEFactory.NewSubfileTypeDE(2)); // 254 single page of multipage file
      ifd.add(new DEFactory.ImageWidthDE(ww)); // 256
      ifd.add(new DEFactory.ImageLengthDE(hh)); // 257

      DEFactory.BitsPerSampleDE bpss = new DEFactory.BitsPerSampleDE(3);
      bpss.setBitsPerSample(0, 8); // Y
      bpss.setBitsPerSample(1, 8); // Cb
      bpss.setBitsPerSample(2, 8); // Cr
      ifd.add(bpss); // 258

      ifd.add(new DEFactory.CompressionDE(comp)); // 259
      ifd.add(new DEFactory.PhotometricInterpretationDE(YCbCr)); // 262

      int maxrps, maxstripes; // max RowsPerStrip
      if ((1 << 13) <= width) {
        maxrps = 1;
        maxstripes = height; // one row per strip
      } else {
        maxrps = (1 << 13) / width;
        maxstripes = (height + maxrps - 1) / maxrps;
      }
      if (comp == JPEG) {
        maxrps = ((maxrps + 8 * ssv - 1) / (8 * ssv)) * (8 * ssv);
        maxstripes = (height + maxrps - 1) / maxrps;
      }

      DEFactory.StripOffsetsDE offsets = new DEFactory.StripOffsetsDE(maxstripes);
      ifd.add(offsets); // 273
      ifd.add(new DEFactory.SamplesPerPixelDE(3)); // 277
      ifd.add(new DEFactory.RowsPerStripDE(maxrps)); // 278
      DEFactory.StripByteCountsDE counts = new DEFactory.StripByteCountsDE(maxstripes);
      ifd.add(counts); // 279

      if (param == null) {
        ifd.add(new DEFactory.XResolutionDE(72.0)); // 282
        ifd.add(new DEFactory.YResolutionDE(72.0)); // 283
      } else {
        ifd.add(new DEFactory.XResolutionDE(param.getXResolution())); // 282
        ifd.add(new DEFactory.YResolutionDE(param.getYResolution())); // 283
      }
      ifd.add(new DEFactory.ResolutionUnitDE(Inch)); // 296

      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      OutputStream os = baos;
      JPEGOutputStream jpegos = null;

      if (comp == JPEG) {
        jpegos = new JPEGOutputStream(baos);

        int quality = (param == null) ? 50 : (int) (param.getCompressionQuality() * 100);

        jpegos.setZZQuantizationTable(0, JPEGConstants.LQT, quality);
        jpegos.setZZQuantizationTable(1, JPEGConstants.CQT, quality);

        jpegos.setRawDCHuffmanTable(0, JPEGConstants.HLDCTable);
        jpegos.setRawACHuffmanTable(0, JPEGConstants.HLACTable);
        jpegos.setRawDCHuffmanTable(1, JPEGConstants.HCDCTable);
        jpegos.setRawACHuffmanTable(1, JPEGConstants.HCACTable);

        jpegos.defineQuantizationTables();
        jpegos.defineHuffmanTables();
        jpegos.close();

        DEFactory.JPEGTablesDE jpegtables = new DEFactory.JPEGTablesDE(baos.toByteArray());
        ifd.add(jpegtables); // 347

        baos.reset();

        os = jpegos;
      }

      //      CCIR Recommendation 601-1  LumaRed=299/1000 LumaGreen=587/1000 LumeBlue=114/1000
      //      Y  = ( LumaRed * R + LumaGreen * G + LumaBlue * B )
      //      Cb = ( B - Y ) / ( 2 - 2 * LumaBlue )
      //      Cr = ( R - Y ) / ( 2 - 2 * LumaRed )

      double LumaRed = 299.0 / 1000.0;
      double LumaGreen = 587.0 / 1000.0;
      double LumaBlue = 114.0 / 1000.0;

      DEFactory.YCbCrCoefficientsDE YCbCrCoeff = new DEFactory.YCbCrCoefficientsDE();
      YCbCrCoeff.setLumaRed(LumaRed); // Y
      YCbCrCoeff.setLumaGreen(LumaGreen); // Cb
      YCbCrCoeff.setLumaBlue(LumaBlue); // Cr
      ifd.add(YCbCrCoeff); // 529

      DEFactory.YCbCrSubSamplingDE YCbCrSubSampling = new DEFactory.YCbCrSubSamplingDE();
      YCbCrSubSampling.setHoriz(ssh);
      YCbCrSubSampling.setVert(ssv);
      ifd.add(YCbCrSubSampling); // 530

      double RfBY = 0;
      double RfWY = 255;
      double RfBCb = 128;
      double RfWCb = 255;
      double RfBCr = 128;
      double RfWCr = 255;

      DEFactory.ReferenceBlackWhiteDE ReferenceBlackWhite = new DEFactory.ReferenceBlackWhiteDE();
      ReferenceBlackWhite.setY(RfBY, RfWY);
      ReferenceBlackWhite.setCb(RfBCb, RfWCb);
      ReferenceBlackWhite.setCr(RfBCr, RfWCr);
      ifd.add(ReferenceBlackWhite); // 532

      TIFFYCbCrOutputStream ycbcros;
      if (jpegos == null) {
        ycbcros = new TIFFYCbCrOutputStream(os, width, ssv, ssh);
        os = new TIFFSubSamplingOutputStream(ycbcros, width, ssv, ssh);
      } else {
        ycbcros = new TIFFYCbCrOutputStream(os, width, 1, 1); // jpeg does own subsampling
        os = ycbcros;
      }

      ycbcros.setPositioning(1);
      ycbcros.setColourCoefficients(LumaRed, LumaGreen, LumaBlue);
      ycbcros.setRfBWY(RfBY, RfWY);
      ycbcros.setRfBWCb(RfBCb, RfWCb);
      ycbcros.setRfBWCr(RfBCr, RfWCr);

      WritableRaster raster = image.getRaster();
      DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer();
      int[] imgdata = (int[]) buffer.getData();

      int c = 0, index = 0;
      for (int y = 0; y < height; y += maxrps) {

        if ((height - y) < maxrps) {
          maxrps = height - y;
        }

        if (jpegos != null) {
          jpegos.startOfImage();
          int[] hv = {(ssh << 4) | ssv, 0x11, 0x11}; // (Hi<<4)|Vi
          int[] q = {0, 1, 1}; // quantization table Y=0, Cb=Cr=1
          //          jpegos.startOfFrame(((maxrps+ssv-1)/ssv)*ssv,ww,hv,q);
          jpegos.startOfFrame(maxrps, ww, hv, q);
          int[] sel = {0, 1, 1}; // DC,AC code table Y=0, Cb=Cr=1
          jpegos.startOfScan(sel);
        }

        for (int i = 0; i < maxrps; i++) {
          int x = 0;
          while (x < width) {
            c = imgdata[x + (y + i) * width];
            //            c = image.getRGB(x,y+i);
            os.write((c >> 16) & 0x000000FF);
            os.write((c >> 8) & 0x000000FF);
            os.write(c & 0x000000FF);
            x++;
          }
          while (x < ww) {
            os.write((c >> 16) & 0x000000FF);
            os.write((c >> 8) & 0x000000FF);
            os.write(c & 0x000000FF);
            x++;
          }
        }
        os.close();

        byte[] data = baos.toByteArray();
        counts.setCount(index, data.length); // update ifd strip counter array
        offsets.setOffset(index, out.getStreamPosition()); // update ifd image data offset array
        out.write(data); // write to image stream
        baos.reset();
        index++;
      }
      return ifd;
    } catch (Exception e) {
      e.printStackTrace();
      throw new IOException(getClass().getName() + ".writeYCbCrImage:\n\t" + e.getMessage());
    }
  }