Package org.apache.sanselan.formats.pcx

Source Code of org.apache.sanselan.formats.pcx.PcxWriter

/*
*  Licensed 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.
*  under the License.
*/

package org.apache.sanselan.formats.pcx;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.common.BinaryOutputStream;
import org.apache.sanselan.palette.PaletteFactory;
import org.apache.sanselan.palette.SimplePalette;

public class PcxWriter implements PcxConstants
{
    private int encoding;
    private int bitDepth = -1;

    public PcxWriter(Map params) throws ImageWriteException
    {
        // make copy of params; we'll clear keys as we consume them.
        params = (params == null) ? new HashMap() : new HashMap(params);

        // clear format key.
        if (params.containsKey(PARAM_KEY_FORMAT))
            params.remove(PARAM_KEY_FORMAT);

        // uncompressed PCX files are not even documented in ZSoft's spec,
        // let alone supported by most image viewers
        encoding = PcxImageParser.PcxHeader.ENCODING_RLE;
        if (params.containsKey(PARAM_KEY_PCX_COMPRESSION))
        {
            Object value = params.remove(PARAM_KEY_PCX_COMPRESSION);
            if (value != null)
            {
                if (!(value instanceof Number))
                    throw new ImageWriteException(
                            "Invalid compression parameter: " + value);
                int compression = ((Number) value).intValue();
                if (compression == PCX_COMPRESSION_UNCOMPRESSED)
                    encoding = PcxImageParser.PcxHeader.ENCODING_UNCOMPRESSED;
            }
        }

        if (params.containsKey(PARAM_KEY_PCX_BIT_DEPTH))
        {
            Object value = params.remove(PARAM_KEY_PCX_BIT_DEPTH);
            if (value != null)
            {
                if (!(value instanceof Number))
                    throw new ImageWriteException(
                            "Invalid bit depth parameter: " + value);
                bitDepth = ((Number)value).intValue();
            }
        }

        if (params.size() > 0)
        {
            Object firstKey = params.keySet().iterator().next();
            throw new ImageWriteException("Unknown parameter: " + firstKey);
        }
    }

    private void writeScanLine(BinaryOutputStream bos, byte[] scanline)
            throws IOException, ImageWriteException
    {
        if (encoding == PcxImageParser.PcxHeader.ENCODING_UNCOMPRESSED)
            bos.writeByteArray(scanline);
        else
        {
            if (encoding == PcxImageParser.PcxHeader.ENCODING_RLE)
            {
                int previousByte = -1;
                int repeatCount = 0;
                for (int i = 0; i < scanline.length; i++)
                {
                    if ((scanline[i] & 0xff) == previousByte && repeatCount < 63)
                        ++repeatCount;
                    else
                    {
                        if (repeatCount > 0)
                        {
                            if (repeatCount == 1 && (previousByte & 0xc0) != 0xc0)
                                bos.write(previousByte);
                            else
                            {
                                bos.write(0xc0 | repeatCount);
                                bos.write(previousByte);
                            }
                        }
                        previousByte = 0xff & scanline[i];
                        repeatCount = 1;
                    }
                }
                if (repeatCount > 0)
                {
                    if (repeatCount == 1 && (previousByte & 0xc0) != 0xc0)
                        bos.write(previousByte);
                    else
                    {
                        bos.write(0xc0 | repeatCount);
                        bos.write(previousByte);
                    }
                }
            }
            else
                throw new ImageWriteException("Invalid PCX encoding " + encoding);
        }
    }

    public void writeImage(BufferedImage src, OutputStream os)
            throws ImageWriteException, IOException
    {
        final PaletteFactory paletteFactory = new PaletteFactory();
        final SimplePalette palette = paletteFactory.makePaletteSimple(src, 256);
        BinaryOutputStream bos = new BinaryOutputStream(os, BinaryOutputStream.BYTE_ORDER_INTEL);
        if (palette == null || bitDepth == 24 || bitDepth == 32)
        {
            if (bitDepth == 32)
                write32BppPCX(src, bos);
            else
                write24BppPCX(src, bos);
        }
        else if (palette.length() > 16 || bitDepth == 8)
            write256ColorPCX(src, palette, bos);
        else if (palette.length() > 2 || bitDepth == 4)
            write16ColorPCX(src, palette, bos);
        else
        {
            boolean onlyBlackAndWhite = true;
            if (palette.length() >= 1)
            {
                int rgb = palette.getEntry(0);
                if (rgb != 0 && rgb != 0xffffff)
                    onlyBlackAndWhite = false;
            }
            if (palette.length() == 2)
            {
                int rgb = palette.getEntry(1);
                if (rgb != 0 && rgb != 0xffffff)
                    onlyBlackAndWhite = false;
            }
            if (onlyBlackAndWhite)
                writeBlackAndWhitePCX(src, palette, bos);
            else
                write16ColorPCX(src, palette, bos);
        }
    }

    private void write32BppPCX(BufferedImage src, BinaryOutputStream bos)
            throws ImageWriteException, IOException
    {
        final int bytesPerLine = src.getWidth() % 2 == 0 ?
            src.getWidth() : src.getWidth() + 1;

        // PCX header
        bos.write(10); // manufacturer
        bos.write(5); // version
        bos.write(encoding); // encoding
        bos.write(32); // bits per pixel
        bos.write2Bytes(0); // xMin
        bos.write2Bytes(0); // yMin
        bos.write2Bytes(src.getWidth() - 1); // xMax
        bos.write2Bytes(src.getHeight() - 1); // yMax
        bos.write2Bytes(300); // hDpi
        bos.write2Bytes(300); // vDpi
        bos.writeByteArray(new byte[48]); // 16 color palette
        bos.write(0); // reserved
        bos.write(1); // planes
        bos.write2Bytes(bytesPerLine); // bytes per line
        bos.write2Bytes(1); // palette info
        bos.write2Bytes(0); // hScreenSize
        bos.write2Bytes(0); // vScreenSize
        bos.writeByteArray(new byte[54]);

        int rgbs[] = new int[src.getWidth()];
        byte rgbBytes[] = new byte[4 * bytesPerLine];
        for (int y = 0; y < src.getHeight(); y++)
        {
            src.getRGB(0, y, src.getWidth(), 1, rgbs, 0, src.getWidth());
            for (int x = 0; x < rgbs.length; x++)
            {
                rgbBytes[4*x + 0] = (byte) (rgbs[x] & 0xff);
                rgbBytes[4*x + 1] = (byte) ((rgbs[x] >> 8) & 0xff);
                rgbBytes[4*x + 2] = (byte) ((rgbs[x] >> 16) & 0xff);
                rgbBytes[4*x + 3] = 0;
            }
            writeScanLine(bos, rgbBytes);
        }
    }

    private void write24BppPCX(BufferedImage src, BinaryOutputStream bos)
            throws ImageWriteException, IOException
    {
        final int bytesPerLine = src.getWidth() % 2 == 0 ?
            src.getWidth() : src.getWidth() + 1;
       
        // PCX header
        bos.write(10); // manufacturer
        bos.write(5); // version
        bos.write(encoding); // encoding
        bos.write(8); // bits per pixel
        bos.write2Bytes(0); // xMin
        bos.write2Bytes(0); // yMin
        bos.write2Bytes(src.getWidth() - 1); // xMax
        bos.write2Bytes(src.getHeight() - 1); // yMax
        bos.write2Bytes(300); // hDpi
        bos.write2Bytes(300); // vDpi
        bos.writeByteArray(new byte[48]); // 16 color palette
        bos.write(0); // reserved
        bos.write(3); // planes
        bos.write2Bytes(bytesPerLine); // bytes per line
        bos.write2Bytes(1); // palette info
        bos.write2Bytes(0); // hScreenSize
        bos.write2Bytes(0); // vScreenSize
        bos.writeByteArray(new byte[54]);

        int rgbs[] = new int[src.getWidth()];
        byte rgbBytes[] = new byte[3 * bytesPerLine];
        for (int y = 0; y < src.getHeight(); y++)
        {
            src.getRGB(0, y, src.getWidth(), 1, rgbs, 0, src.getWidth());
            for (int x = 0; x < rgbs.length; x++)
            {
                rgbBytes[x] = (byte) ((rgbs[x] >> 16) & 0xff);
                rgbBytes[bytesPerLine + x] = (byte) ((rgbs[x] >> 8) & 0xff);
                rgbBytes[2 * bytesPerLine + x] = (byte) (rgbs[x] & 0xff);
            }
            writeScanLine(bos, rgbBytes);
        }
    }

    private void writeBlackAndWhitePCX(BufferedImage src, SimplePalette palette, BinaryOutputStream bos)
            throws ImageWriteException, IOException
    {
        int bytesPerLine = (src.getWidth() + 7) / 8;
        if (bytesPerLine % 2 != 0)
            ++bytesPerLine;

        // PCX header
        bos.write(10); // manufacturer
        bos.write(3); // version - it seems some apps only open
        // black and white files if the version is 3...
        bos.write(encoding); // encoding
        bos.write(1); // bits per pixel
        bos.write2Bytes(0); // xMin
        bos.write2Bytes(0); // yMin
        bos.write2Bytes(src.getWidth() - 1); // xMax
        bos.write2Bytes(src.getHeight() - 1); // yMax
        bos.write2Bytes(300); // hDpi
        bos.write2Bytes(300); // vDpi
        bos.writeByteArray(new byte[48]); // 16 color palette
        bos.write(0); // reserved
        bos.write(1); // planes
        bos.write2Bytes(bytesPerLine); // bytes per line
        bos.write2Bytes(1); // palette info
        bos.write2Bytes(0); // hScreenSize
        bos.write2Bytes(0); // vScreenSize
        bos.writeByteArray(new byte[54]);

        byte[] row = new byte[bytesPerLine];
        for (int y = 0; y < src.getHeight(); y++)
        {
            Arrays.fill(row, (byte)0);
            for (int x = 0; x < src.getWidth(); x++)
            {
                int rgb = 0xffffff & src.getRGB(x, y);
                int bit;
                if (rgb == 0x000000)
                    bit = 0;
                else if (rgb == 0xffffff)
                    bit = 1;
                else
                    throw new ImageWriteException("Pixel neither black nor white");
                row[x / 8] |= (bit << (7 - (x % 8)));
            }
            writeScanLine(bos, row);
        }
    }

    private void write16ColorPCX(BufferedImage src, SimplePalette palette, BinaryOutputStream bos)
            throws ImageWriteException, IOException
    {
        int bytesPerLine = (src.getWidth() + 1) / 2;
        if (bytesPerLine % 2 != 0)
            ++bytesPerLine;

        byte[] palette16 = new byte[16*3];
        for (int i = 0; i < 16; i++)
        {
            int rgb;
            if (i < palette.length())
                rgb = palette.getEntry(i);
            else
                rgb = 0;
            palette16[3*i + 0] = (byte) (0xff & (rgb >> 16));
            palette16[3*i + 1] = (byte) (0xff & (rgb >> 8));
            palette16[3*i + 2] = (byte) (0xff & rgb);
        }

        // PCX header
        bos.write(10); // manufacturer
        bos.write(5); // version
        bos.write(encoding); // encoding
        bos.write(4); // bits per pixel
        bos.write2Bytes(0); // xMin
        bos.write2Bytes(0); // yMin
        bos.write2Bytes(src.getWidth() - 1); // xMax
        bos.write2Bytes(src.getHeight() - 1); // yMax
        bos.write2Bytes(300); // hDpi
        bos.write2Bytes(300); // vDpi
        bos.writeByteArray(palette16); // 16 color palette
        bos.write(0); // reserved
        bos.write(1); // planes
        bos.write2Bytes(bytesPerLine); // bytes per line
        bos.write2Bytes(1); // palette info
        bos.write2Bytes(0); // hScreenSize
        bos.write2Bytes(0); // vScreenSize
        bos.writeByteArray(new byte[54]);

        byte[] indeces = new byte[bytesPerLine];
        for (int y = 0; y < src.getHeight(); y++)
        {
            Arrays.fill(indeces, (byte)0);
            for (int x = 0; x < src.getWidth(); x++)
            {
                int argb = src.getRGB(x, y);
                int index = palette.getPaletteIndex(0xffffff & argb);
                indeces[x / 2] |= (index << 4*(1 - (x % 2)));
            }
            writeScanLine(bos, indeces);
        }
    }

    private void write256ColorPCX(BufferedImage src, SimplePalette palette, BinaryOutputStream bos)
            throws ImageWriteException, IOException
    {
        final int bytesPerLine = src.getWidth() % 2 == 0 ?
            src.getWidth() : src.getWidth() + 1;

        // PCX header
        bos.write(10); // manufacturer
        bos.write(5); // version
        bos.write(encoding); // encoding
        bos.write(8); // bits per pixel
        bos.write2Bytes(0); // xMin
        bos.write2Bytes(0); // yMin
        bos.write2Bytes(src.getWidth() - 1); // xMax
        bos.write2Bytes(src.getHeight() - 1); // yMax
        bos.write2Bytes(300); // hDpi
        bos.write2Bytes(300); // vDpi
        bos.writeByteArray(new byte[48]); // 16 color palette
        bos.write(0); // reserved
        bos.write(1); // planes
        bos.write2Bytes(bytesPerLine); // bytes per line
        bos.write2Bytes(1); // palette info
        bos.write2Bytes(0); // hScreenSize
        bos.write2Bytes(0); // vScreenSize
        bos.writeByteArray(new byte[54]);

        byte[] indeces = new byte[bytesPerLine];
        for (int y = 0; y < src.getHeight(); y++)
        {
            for (int x = 0; x < src.getWidth(); x++)
            {
                int argb = src.getRGB(x, y);
                int index = palette.getPaletteIndex(0xffffff & argb);
                indeces[x] = (byte) index;
            }
            writeScanLine(bos, indeces);
        }
        // palette
        bos.write(12);
        for (int i = 0; i < 256; i++)
        {
            int rgb;
            if (i < palette.length())
                rgb = palette.getEntry(i);
            else
                rgb = 0;
            bos.write((rgb >> 16) & 0xff);
            bos.write((rgb >> 8) & 0xff);
            bos.write(rgb & 0xff);
        }
    }
}
TOP

Related Classes of org.apache.sanselan.formats.pcx.PcxWriter

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.