Package org.apache.sanselan.formats.tiff.write

Source Code of org.apache.sanselan.formats.tiff.write.TiffImageWriterLossless$BufferOutputStream

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sanselan.formats.tiff.write;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import org.apache.sanselan.FormatCompliance;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.common.BinaryFileFunctions;
import org.apache.sanselan.common.BinaryOutputStream;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.common.byteSources.ByteSourceArray;
import org.apache.sanselan.formats.tiff.JpegImageData;
import org.apache.sanselan.formats.tiff.TiffContents;
import org.apache.sanselan.formats.tiff.TiffDirectory;
import org.apache.sanselan.formats.tiff.TiffElement;
import org.apache.sanselan.formats.tiff.TiffField;
import org.apache.sanselan.formats.tiff.TiffImageData;
import org.apache.sanselan.formats.tiff.TiffReader;
import org.apache.sanselan.util.Debug;

public class TiffImageWriterLossless extends TiffImageWriterBase
{
    private final byte exifBytes[];

    public TiffImageWriterLossless(byte exifBytes[])
    {
        this.exifBytes = exifBytes;
    }

    public TiffImageWriterLossless(int byteOrder, byte exifBytes[])
    {
        super(byteOrder);
        this.exifBytes = exifBytes;
    }

    //    private static class TiffPiece
    //    {
    //        public final int offset;
    //        public final int length;
    //
    //        public TiffPiece(final int offset, final int length)
    //        {
    //            this.offset = offset;
    //            this.length = length;
    //        }
    //    }

    private void dumpElements(List elements) throws IOException
    {
        //        try
        //        {
        ByteSource byteSource = new ByteSourceArray(exifBytes);

        dumpElements(byteSource, elements);
        //        }
        //        catch (ImageReadException e)
        //        {
        //            throw new ImageWriteException(e.getMessage(), e);
        //        }
    }

    private void dumpElements(ByteSource byteSource, List elements)
            throws IOException
    {
        int last = TIFF_HEADER_SIZE;
        for (int i = 0; i < elements.size(); i++)
        {
            TiffElement element = (TiffElement) elements.get(i);
            if (element.offset > last)
            {
                final int SLICE_SIZE = 32;
                int gepLength = element.offset - last;
                Debug.debug("gap of " + gepLength + " bytes.");
                byte bytes[] = byteSource.getBlock(last, gepLength);
                if (bytes.length > 2 * SLICE_SIZE)
                {
                    Debug.debug("\t" + "head", BinaryFileFunctions.head(bytes,
                            SLICE_SIZE));
                    Debug.debug("\t" + "tail", BinaryFileFunctions.tail(bytes,
                            SLICE_SIZE));
                }
                else
                    Debug.debug("\t" + "bytes", bytes);
            }

            Debug.debug("element[" + i + "]:" + element.getElementDescription()
                    + " (" + element.offset + " + " + element.length + " = "
                    + (element.offset + element.length) + ")");
            if (element instanceof TiffDirectory)
            {
                TiffDirectory dir = (TiffDirectory) element;
                Debug.debug("\t" + "next Directory Offset: "
                        + dir.nextDirectoryOffset);
            }
            last = element.offset + element.length;
        }
        Debug.debug();
    }

    private List analyzeOldTiff() throws ImageWriteException, IOException
    {
        try
        {
            ByteSource byteSource = new ByteSourceArray(exifBytes);
            Map params = null;
            FormatCompliance formatCompliance = FormatCompliance.getDefault();
            TiffContents contents = new TiffReader(false).readContents(byteSource,
                    params, formatCompliance);

            ArrayList elements = new ArrayList();
            //            result.add(contents.header); // ?

            List directories = contents.directories;
            for (int d = 0; d < directories.size(); d++)
            {
                TiffDirectory directory = (TiffDirectory) directories.get(d);
                elements.add(directory);

                List fields = directory.getDirectoryEntrys();
                for (int f = 0; f < fields.size(); f++)
                {
                    TiffField field = (TiffField) fields.get(f);
                    TiffElement oversizeValue = field.getOversizeValueElement();
                    if (oversizeValue != null)
                        elements.add(oversizeValue);

                }

                JpegImageData jpegImageData = directory.getJpegImageData();
                if (jpegImageData != null)
                    elements.add(jpegImageData);

                TiffImageData tiffImageData = directory.getTiffImageData();
                if (tiffImageData != null)
                {
                    TiffElement.DataElement data[] = tiffImageData
                            .getImageData();
                    for (int i = 0; i < data.length; i++)
                        elements.add(data[i]);
                }
            }

            Collections.sort(elements, TiffElement.COMPARATOR);

            //            dumpElements(byteSource, elements);

            List result = new ArrayList();
            {
                final int TOLERANCE = 3;
                //                int last = TIFF_HEADER_SIZE;
                TiffElement start = null;
                int index = -1;
                for (int i = 0; i < elements.size(); i++)
                {
                    TiffElement element = (TiffElement) elements.get(i);
                    int lastElementByte = element.offset + element.length;
                    if (start == null)
                    {
                        start = element;
                        index = lastElementByte;
                    }
                    else if (element.offset - index > TOLERANCE)
                    {
                        result.add(new TiffElement.Stub(start.offset, index
                                - start.offset));
                        start = element;
                        index = lastElementByte;
                    }
                    else
                    {
                        index = lastElementByte;
                    }
                }
                if (null != start)
                    result.add(new TiffElement.Stub(start.offset, index
                            - start.offset));
            }

            //            dumpElements(byteSource, result);

            return result;
        }
        catch (ImageReadException e)
        {
            throw new ImageWriteException(e.getMessage(), e);
        }
    }

    public void write(OutputStream os, TiffOutputSet outputSet)
            throws IOException, ImageWriteException
    {
        List analysis = analyzeOldTiff();
        int oldLength = exifBytes.length;
        if (analysis.size() < 1)
            throw new ImageWriteException("Couldn't analyze old tiff data.");
        else if (analysis.size() == 1)
        {
            TiffElement onlyElement = (TiffElement) analysis.get(0);
            //            Debug.debug("onlyElement", onlyElement.getElementDescription());
            if (onlyElement.offset == TIFF_HEADER_SIZE
                    && onlyElement.offset + onlyElement.length
                            + TIFF_HEADER_SIZE == oldLength)
            {
                // no gaps in old data, safe to complete overwrite.
                new TiffImageWriterLossy(byteOrder).write(os, outputSet);
                return;
            }
        }

        //        if (true)
        //            throw new ImageWriteException("hahah");

        //        List directories = outputSet.getDirectories();

        TiffOutputSummary outputSummary = validateDirectories(outputSet);

        List outputItems = outputSet.getOutputItems(outputSummary);

        int outputLength = updateOffsetsStep(analysis, outputItems);
        //        Debug.debug("outputLength", outputLength);

        outputSummary.updateOffsets(byteOrder);

        writeStep(os, outputSet, analysis, outputItems, outputLength);

    }

    private static final Comparator ELEMENT_SIZE_COMPARATOR = new Comparator()
    {
        public int compare(Object o1, Object o2)
        {
            TiffElement e1 = (TiffElement) o1;
            TiffElement e2 = (TiffElement) o2;
            return e1.length - e2.length;
        }
    };

    private static final Comparator ITEM_SIZE_COMPARATOR = new Comparator()
    {
        public int compare(Object o1, Object o2)
        {
            TiffOutputItem e1 = (TiffOutputItem) o1;
            TiffOutputItem e2 = (TiffOutputItem) o2;
            return e1.getItemLength() - e2.getItemLength();
        }
    };

    private int updateOffsetsStep(List analysis, List outputItems)
            throws IOException, ImageWriteException
    {
        // items we cannot fit into a gap, we shall append to tail.
        int overflowIndex = exifBytes.length;

        // make copy.
        List unusedElements = new ArrayList(analysis);

        // should already be in order of offset, but make sure.
        Collections.sort(unusedElements, TiffElement.COMPARATOR);
        Collections.reverse(unusedElements);
        // any items that represent a gap at the end of the exif segment, can be discarded.
        while (unusedElements.size() > 0)
        {
            TiffElement element = (TiffElement) unusedElements.get(0);
            int elementEnd = element.offset + element.length;
            if (elementEnd == overflowIndex)
            {
                // discarding a tail element.  should only happen once.
                overflowIndex -= element.length;
                unusedElements.remove(0);
            }
            else
                break;
        }

        Collections.sort(unusedElements, ELEMENT_SIZE_COMPARATOR);
        Collections.reverse(unusedElements);

        //        Debug.debug("unusedElements");
        //        dumpElements(unusedElements);

        // make copy.
        List unplacedItems = new ArrayList(outputItems);
        Collections.sort(unplacedItems, ITEM_SIZE_COMPARATOR);
        Collections.reverse(unplacedItems);

        while (unplacedItems.size() > 0)
        {
            // pop off largest unplaced item.
            TiffOutputItem outputItem = (TiffOutputItem) unplacedItems
                    .remove(0);
            int outputItemLength = outputItem.getItemLength();
            //            Debug.debug("largest unplaced item: "
            //                    + outputItem.getItemDescription() + " (" + outputItemLength
            //                    + ")");

            // search for the smallest possible element large enough to hold the item.
            TiffElement bestFit = null;
            for (int i = 0; i < unusedElements.size(); i++)
            {
                TiffElement element = (TiffElement) unusedElements.get(i);
                if (element.length >= outputItemLength)
                    bestFit = element;
                else
                    break;
            }
            if (null == bestFit)
            {
                // we couldn't place this item.  overflow.
                outputItem.setOffset(overflowIndex);
                overflowIndex += outputItemLength;
            }
            else
            {
                outputItem.setOffset(bestFit.offset);
                unusedElements.remove(bestFit);

                if (bestFit.length > outputItemLength)
                {
                    // not a perfect fit.
                    int excessOffset = bestFit.offset + outputItemLength;
                    int excessLength = bestFit.length - outputItemLength;
                    unusedElements.add(new TiffElement.Stub(excessOffset,
                            excessLength));
                    // make sure the new element is in the correct order.
                    Collections.sort(unusedElements, ELEMENT_SIZE_COMPARATOR);
                    Collections.reverse(unusedElements);
                }
            }
        }

        return overflowIndex;
        //
        //        if (true)
        //            throw new IOException("mew");
        //
        //        //        int offset = TIFF_HEADER_SIZE;
        //        int offset = exifBytes.length;
        //
        //        for (int i = 0; i < outputItems.size(); i++)
        //        {
        //            TiffOutputItem outputItem = (TiffOutputItem) outputItems.get(i);
        //
        //            outputItem.setOffset(offset);
        //            int itemLength = outputItem.getItemLength();
        //            offset += itemLength;
        //
        //            int remainder = imageDataPaddingLength(itemLength);
        //            offset += remainder;
        //        }
    }
    private static class BufferOutputStream extends OutputStream
    {
        private final byte buffer[];
        private int index;

        public BufferOutputStream(final byte[] buffer, final int index)
        {
            this.buffer = buffer;
            this.index = index;
        }

        public void write(int b) throws IOException
        {
            if (index >= buffer.length)
                throw new IOException("Buffer overflow.");

            buffer[index++] = (byte) b;
        }

        public void write(byte b[], int off, int len) throws IOException
        {
            if (index + len > buffer.length)
                throw new IOException("Buffer overflow.");
            System.arraycopy(b, off, buffer, index, len);
            index += len;
        }
    }

    private void writeStep(OutputStream os, TiffOutputSet outputSet,
            List analysis, List outputItems, int outputLength)
            throws IOException, ImageWriteException
    {
        TiffOutputDirectory rootDirectory = outputSet.getRootDirectory();

        byte output[] = new byte[outputLength];

        // copy old data (including maker notes, etc.)
        System.arraycopy(exifBytes, 0, output, 0, Math.min(exifBytes.length,
                output.length));

        //        bos.write(exifBytes, TIFF_HEADER_SIZE, exifBytes.length
        //        - TIFF_HEADER_SIZE);

        {
            BufferOutputStream tos = new BufferOutputStream(output, 0);
            BinaryOutputStream bos = new BinaryOutputStream(tos, byteOrder);
            writeImageFileHeader(bos, rootDirectory.getOffset());
        }

        // zero out the parsed pieces of old exif segment, in case we don't overwrite them.
        for (int i = 0; i < analysis.size(); i++)
        {
            TiffElement element = (TiffElement) analysis.get(i);
            for (int j = 0; j < element.length; j++)
            {
                int index = element.offset + j;
                if (index < output.length)
                    output[index] = 0;
            }
        }

        // write in the new items
        for (int i = 0; i < outputItems.size(); i++)
        {
            TiffOutputItem outputItem = (TiffOutputItem) outputItems.get(i);

            BufferOutputStream tos = new BufferOutputStream(output, outputItem
                    .getOffset());
            BinaryOutputStream bos = new BinaryOutputStream(tos, byteOrder);
            outputItem.writeItem(bos);
        }

        os.write(output);
    }

}
TOP

Related Classes of org.apache.sanselan.formats.tiff.write.TiffImageWriterLossless$BufferOutputStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.