001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.imaging.formats.dcx; 018 019import static org.apache.commons.imaging.common.BinaryFunctions.read4Bytes; 020 021import java.awt.Dimension; 022import java.awt.image.BufferedImage; 023import java.io.IOException; 024import java.io.InputStream; 025import java.io.OutputStream; 026import java.io.PrintWriter; 027import java.nio.ByteOrder; 028import java.util.ArrayList; 029import java.util.List; 030 031import org.apache.commons.imaging.AbstractImageParser; 032import org.apache.commons.imaging.ImageFormat; 033import org.apache.commons.imaging.ImageFormats; 034import org.apache.commons.imaging.ImageInfo; 035import org.apache.commons.imaging.ImagingException; 036import org.apache.commons.imaging.bytesource.ByteSource; 037import org.apache.commons.imaging.common.Allocator; 038import org.apache.commons.imaging.common.BinaryOutputStream; 039import org.apache.commons.imaging.common.ImageMetadata; 040import org.apache.commons.imaging.formats.pcx.PcxImageParser; 041import org.apache.commons.imaging.formats.pcx.PcxImagingParameters; 042 043public class DcxImageParser extends AbstractImageParser<PcxImagingParameters> { 044 private static final class DcxHeader { 045 046 public static final int DCX_ID = 0x3ADE68B1; 047 public final int id; 048 public final long[] pageTable; 049 050 DcxHeader(final int id, final long[] pageTable) { 051 this.id = id; 052 this.pageTable = pageTable; 053 } 054 055 public void dump(final PrintWriter pw) { 056 pw.println("DcxHeader"); 057 pw.println("Id: 0x" + Integer.toHexString(id)); 058 pw.println("Pages: " + pageTable.length); 059 pw.println(); 060 } 061 } 062 063 // See [BROEKN URL] http://www.fileformat.fine/format/pcx/egff.htm for documentation 064 private static final String DEFAULT_EXTENSION = ImageFormats.DCX.getDefaultExtension(); 065 066 private static final String[] ACCEPTED_EXTENSIONS = ImageFormats.DCX.getExtensions(); 067 068 public DcxImageParser() { 069 super(ByteOrder.LITTLE_ENDIAN); 070 } 071 072 @Override 073 public boolean dumpImageFile(final PrintWriter pw, final ByteSource byteSource) throws ImagingException, IOException { 074 readDcxHeader(byteSource).dump(pw); 075 return true; 076 } 077 078 @Override 079 protected String[] getAcceptedExtensions() { 080 return ACCEPTED_EXTENSIONS; 081 } 082 083 @Override 084 protected ImageFormat[] getAcceptedTypes() { 085 return new ImageFormat[] { ImageFormats.DCX }; 086 } 087 088 @Override 089 public List<BufferedImage> getAllBufferedImages(final ByteSource byteSource) throws ImagingException, IOException { 090 final DcxHeader dcxHeader = readDcxHeader(byteSource); 091 final List<BufferedImage> images = new ArrayList<>(); 092 final PcxImageParser pcxImageParser = new PcxImageParser(); 093 for (final long element : dcxHeader.pageTable) { 094 try (InputStream stream = byteSource.getInputStream(element)) { 095 final ByteSource pcxSource = ByteSource.inputStream(stream, null); 096 final BufferedImage image = pcxImageParser.getBufferedImage(pcxSource, new PcxImagingParameters()); 097 images.add(image); 098 } 099 } 100 return images; 101 } 102 103 @Override 104 public final BufferedImage getBufferedImage(final ByteSource byteSource, final PcxImagingParameters params) throws ImagingException, IOException { 105 final List<BufferedImage> list = getAllBufferedImages(byteSource); 106 return list.isEmpty() ? null : list.get(0); 107 } 108 109 @Override 110 public String getDefaultExtension() { 111 return DEFAULT_EXTENSION; 112 } 113 114 @Override 115 public PcxImagingParameters getDefaultParameters() { 116 return new PcxImagingParameters(); 117 } 118 119 // FIXME should throw UOE 120 @Override 121 public byte[] getIccProfileBytes(final ByteSource byteSource, final PcxImagingParameters params) throws ImagingException, IOException { 122 return null; 123 } 124 125 // FIXME should throw UOE 126 @Override 127 public ImageInfo getImageInfo(final ByteSource byteSource, final PcxImagingParameters params) throws ImagingException, IOException { 128 return null; 129 } 130 131 // FIXME should throw UOE 132 @Override 133 public Dimension getImageSize(final ByteSource byteSource, final PcxImagingParameters params) throws ImagingException, IOException { 134 return null; 135 } 136 137 // FIXME should throw UOE 138 @Override 139 public ImageMetadata getMetadata(final ByteSource byteSource, final PcxImagingParameters params) throws ImagingException, IOException { 140 return null; 141 } 142 143 @Override 144 public String getName() { 145 return "Dcx-Custom"; 146 } 147 148 private DcxHeader readDcxHeader(final ByteSource byteSource) throws ImagingException, IOException { 149 try (InputStream is = byteSource.getInputStream()) { 150 final int id = read4Bytes("Id", is, "Not a Valid DCX File", getByteOrder()); 151 final int size = 1024; 152 final List<Long> pageTable = Allocator.arrayList(size); 153 for (int i = 0; i < size; i++) { 154 final long pageOffset = 0xFFFFffffL & read4Bytes("PageTable", is, "Not a Valid DCX File", getByteOrder()); 155 if (pageOffset == 0) { 156 break; 157 } 158 pageTable.add(pageOffset); 159 } 160 161 if (id != DcxHeader.DCX_ID) { 162 throw new ImagingException("Not a Valid DCX File: file id incorrect"); 163 } 164 if (pageTable.size() == size) { 165 throw new ImagingException("DCX page table not terminated by zero entry"); 166 } 167 168 final long[] pages = pageTable.stream().mapToLong(Long::longValue).toArray(); 169 return new DcxHeader(id, pages); 170 } 171 } 172 173 @Override 174 public void writeImage(final BufferedImage src, final OutputStream os, final PcxImagingParameters params) throws ImagingException, IOException { 175 final int headerSize = 4 + 1024 * 4; 176 177 final BinaryOutputStream bos = BinaryOutputStream.littleEndian(os); 178 bos.write4Bytes(DcxHeader.DCX_ID); 179 // Some apps may need a full 1024 entry table 180 bos.write4Bytes(headerSize); 181 for (int i = 0; i < 1023; i++) { 182 bos.write4Bytes(0); 183 } 184 new PcxImageParser().writeImage(src, bos, params); 185 } 186}