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 images.add(pcxImageParser.getBufferedImage(ByteSource.inputStream(stream, null), new PcxImagingParameters())); 096 } 097 } 098 return images; 099 } 100 101 @Override 102 public final BufferedImage getBufferedImage(final ByteSource byteSource, final PcxImagingParameters params) throws ImagingException, IOException { 103 final List<BufferedImage> list = getAllBufferedImages(byteSource); 104 return list.isEmpty() ? null : list.get(0); 105 } 106 107 @Override 108 public String getDefaultExtension() { 109 return DEFAULT_EXTENSION; 110 } 111 112 @Override 113 public PcxImagingParameters getDefaultParameters() { 114 return new PcxImagingParameters(); 115 } 116 117 // FIXME should throw UOE 118 @Override 119 public byte[] getIccProfileBytes(final ByteSource byteSource, final PcxImagingParameters params) throws ImagingException, IOException { 120 return null; 121 } 122 123 // FIXME should throw UOE 124 @Override 125 public ImageInfo getImageInfo(final ByteSource byteSource, final PcxImagingParameters params) throws ImagingException, IOException { 126 return null; 127 } 128 129 // FIXME should throw UOE 130 @Override 131 public Dimension getImageSize(final ByteSource byteSource, final PcxImagingParameters params) throws ImagingException, IOException { 132 return null; 133 } 134 135 // FIXME should throw UOE 136 @Override 137 public ImageMetadata getMetadata(final ByteSource byteSource, final PcxImagingParameters params) throws ImagingException, IOException { 138 return null; 139 } 140 141 @Override 142 public String getName() { 143 return "Dcx-Custom"; 144 } 145 146 private DcxHeader readDcxHeader(final ByteSource byteSource) throws ImagingException, IOException { 147 try (InputStream is = byteSource.getInputStream()) { 148 final int id = read4Bytes("Id", is, "Not a Valid DCX File", getByteOrder()); 149 final int size = 1024; 150 final List<Long> pageTable = Allocator.arrayList(size); 151 for (int i = 0; i < size; i++) { 152 final long pageOffset = 0xFFFFffffL & read4Bytes("PageTable", is, "Not a Valid DCX File", getByteOrder()); 153 if (pageOffset == 0) { 154 break; 155 } 156 pageTable.add(pageOffset); 157 } 158 159 if (id != DcxHeader.DCX_ID) { 160 throw new ImagingException("Not a Valid DCX File: file id incorrect"); 161 } 162 if (pageTable.size() == size) { 163 throw new ImagingException("DCX page table not terminated by zero entry"); 164 } 165 166 final long[] pages = pageTable.stream().mapToLong(Long::longValue).toArray(); 167 return new DcxHeader(id, pages); 168 } 169 } 170 171 @Override 172 public void writeImage(final BufferedImage src, final OutputStream os, final PcxImagingParameters params) throws ImagingException, IOException { 173 final int headerSize = 4 + 1024 * 4; 174 175 final BinaryOutputStream bos = BinaryOutputStream.littleEndian(os); 176 bos.write4Bytes(DcxHeader.DCX_ID); 177 // Some apps may need a full 1024 entry table 178 bos.write4Bytes(headerSize); 179 for (int i = 0; i < 1023; i++) { 180 bos.write4Bytes(0); 181 } 182 new PcxImageParser().writeImage(src, bos, params); 183 } 184}