1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.imaging.formats.pcx;
17
18 import java.awt.image.BufferedImage;
19 import java.io.IOException;
20 import java.io.OutputStream;
21 import java.util.Arrays;
22
23 import org.apache.commons.imaging.PixelDensity;
24 import org.apache.commons.imaging.common.Allocator;
25 import org.apache.commons.imaging.common.BinaryOutputStream;
26 import org.apache.commons.imaging.palette.PaletteFactory;
27 import org.apache.commons.imaging.palette.SimplePalette;
28
29 final class PcxWriter {
30 private final int encoding;
31 private final int bitDepthWanted;
32 private final int planesWanted;
33 private final PixelDensity pixelDensity;
34 private final RleWriter rleWriter;
35
36 PcxWriter(PcxImagingParameters params) {
37
38
39 if (params == null) {
40 params = new PcxImagingParameters();
41 }
42 encoding = params.getCompression() == PcxConstants.PCX_COMPRESSION_UNCOMPRESSED ? PcxImageParser.PcxHeader.ENCODING_UNCOMPRESSED
43 : PcxImageParser.PcxHeader.ENCODING_RLE;
44 rleWriter = new RleWriter(encoding != PcxImageParser.PcxHeader.ENCODING_UNCOMPRESSED);
45 bitDepthWanted = params.getBitDepth();
46 planesWanted = params.getPlanes();
47 final PixelDensity pixelDensityParam = params.getPixelDensity();
48
49 pixelDensity = pixelDensityParam != null ? pixelDensityParam : PixelDensity.createFromPixelsPerInch(72, 72);
50 }
51
52 public void writeImage(final BufferedImage src, final OutputStream os) throws IOException {
53 final PaletteFactory paletteFactory = new PaletteFactory();
54 final SimplePalette palette = paletteFactory.makeExactRgbPaletteSimple(src, 256);
55 final BinaryOutputStream bos = BinaryOutputStream.littleEndian(os);
56 final int bitDepth;
57 final int planes;
58 if (palette == null || bitDepthWanted == 24 || bitDepthWanted == 32) {
59 if (bitDepthWanted == 32) {
60 bitDepth = 32;
61 planes = 1;
62 } else {
63 bitDepth = 8;
64 planes = 3;
65 }
66 } else if (palette.length() > 16 || bitDepthWanted == 8) {
67 bitDepth = 8;
68 planes = 1;
69 } else if (palette.length() > 8 || bitDepthWanted == 4) {
70 if (planesWanted == 1) {
71 bitDepth = 4;
72 planes = 1;
73 } else {
74 bitDepth = 1;
75 planes = 4;
76 }
77 } else if (palette.length() > 4 || bitDepthWanted == 3) {
78 bitDepth = 1;
79 planes = 3;
80 } else if (palette.length() > 2 || bitDepthWanted == 2) {
81 if (planesWanted == 2) {
82 bitDepth = 1;
83 planes = 2;
84 } else {
85 bitDepth = 2;
86 planes = 1;
87 }
88 } else {
89 boolean onlyBlackAndWhite = true;
90 if (palette.length() >= 1) {
91 final int rgb = palette.getEntry(0);
92 if (rgb != 0 && rgb != 0xffffff) {
93 onlyBlackAndWhite = false;
94 }
95 }
96 if (palette.length() == 2) {
97 final int rgb = palette.getEntry(1);
98 if (rgb != 0 && rgb != 0xffffff) {
99 onlyBlackAndWhite = false;
100 }
101 }
102 if (onlyBlackAndWhite) {
103 bitDepth = 1;
104 planes = 1;
105 } else {
106 bitDepth = 1;
107 planes = 2;
108 }
109 }
110
111 int bytesPerLine = (bitDepth * src.getWidth() + 7) / 8;
112 if (bytesPerLine % 2 != 0) {
113
114 bytesPerLine++;
115 }
116
117 final byte[] palette16 = new byte[16 * 3];
118
119 final int paletteLen = palette != null ? palette.length() : 0;
120 for (int i = 0; i < 16; i++) {
121 int rgb;
122 if (i < paletteLen) {
123 rgb = palette.getEntry(i);
124 } else {
125 rgb = 0;
126 }
127 palette16[3 * i + 0] = (byte) (0xff & rgb >> 16);
128 palette16[3 * i + 1] = (byte) (0xff & rgb >> 8);
129 palette16[3 * i + 2] = (byte) (0xff & rgb);
130 }
131
132
133 bos.write(10);
134 bos.write(bitDepth == 1 && planes == 1 ? 3 : 5);
135 bos.write(encoding);
136 bos.write(bitDepth);
137 bos.write2Bytes(0);
138 bos.write2Bytes(0);
139 bos.write2Bytes(src.getWidth() - 1);
140 bos.write2Bytes(src.getHeight() - 1);
141 bos.write2Bytes((short) Math.round(pixelDensity.horizontalDensityInches()));
142 bos.write2Bytes((short) Math.round(pixelDensity.verticalDensityInches()));
143 bos.write(palette16);
144 bos.write(0);
145 bos.write(planes);
146 bos.write2Bytes(bytesPerLine);
147 bos.write2Bytes(1);
148 bos.write2Bytes(0);
149 bos.write2Bytes(0);
150 bos.write(new byte[54]);
151
152 if (bitDepth == 32) {
153 writePixels32(src, bytesPerLine, bos);
154 } else {
155 writePixels(src, bitDepth, planes, bytesPerLine, palette, bos);
156 }
157
158 if (bitDepth == 8 && planes == 1) {
159
160 bos.write(12);
161 for (int i = 0; i < 256; i++) {
162 int rgb;
163 if (i < palette.length()) {
164 rgb = palette.getEntry(i);
165 } else {
166 rgb = 0;
167 }
168 bos.write(rgb >> 16 & 0xff);
169 bos.write(rgb >> 8 & 0xff);
170 bos.write(rgb & 0xff);
171 }
172 }
173 }
174
175 private void writePixels(final BufferedImage src, final int bitDepth, final int planes, final int bytesPerLine, final SimplePalette palette,
176 final BinaryOutputStream bos) throws IOException {
177 final byte[] plane0 = Allocator.byteArray(bytesPerLine);
178 final byte[] plane1 = Allocator.byteArray(bytesPerLine);
179 final byte[] plane2 = Allocator.byteArray(bytesPerLine);
180 final byte[] plane3 = Allocator.byteArray(bytesPerLine);
181 final byte[][] allPlanes = { plane0, plane1, plane2, plane3 };
182
183 for (int y = 0; y < src.getHeight(); y++) {
184 for (int i = 0; i < planes; i++) {
185 Arrays.fill(allPlanes[i], (byte) 0);
186 }
187
188 if (bitDepth == 1 && planes == 1) {
189 for (int x = 0; x < src.getWidth(); x++) {
190 final int rgb = 0xffffff & src.getRGB(x, y);
191 int bit;
192 if (rgb == 0x000000) {
193 bit = 0;
194 } else {
195 bit = 1;
196 }
197 plane0[x >>> 3] |= bit << 7 - (x & 7);
198 }
199 } else if (bitDepth == 1 && planes == 2) {
200 for (int x = 0; x < src.getWidth(); x++) {
201 final int argb = src.getRGB(x, y);
202 final int index = palette.getPaletteIndex(0xffffff & argb);
203 plane0[x >>> 3] |= (index & 1) << 7 - (x & 7);
204 plane1[x >>> 3] |= (index & 2) >> 1 << 7 - (x & 7);
205 }
206 } else if (bitDepth == 1 && planes == 3) {
207 for (int x = 0; x < src.getWidth(); x++) {
208 final int argb = src.getRGB(x, y);
209 final int index = palette.getPaletteIndex(0xffffff & argb);
210 plane0[x >>> 3] |= (index & 1) << 7 - (x & 7);
211 plane1[x >>> 3] |= (index & 2) >> 1 << 7 - (x & 7);
212 plane2[x >>> 3] |= (index & 4) >> 2 << 7 - (x & 7);
213 }
214 } else if (bitDepth == 1 && planes == 4) {
215 for (int x = 0; x < src.getWidth(); x++) {
216 final int argb = src.getRGB(x, y);
217 final int index = palette.getPaletteIndex(0xffffff & argb);
218 plane0[x >>> 3] |= (index & 1) << 7 - (x & 7);
219 plane1[x >>> 3] |= (index & 2) >> 1 << 7 - (x & 7);
220 plane2[x >>> 3] |= (index & 4) >> 2 << 7 - (x & 7);
221 plane3[x >>> 3] |= (index & 8) >> 3 << 7 - (x & 7);
222 }
223 } else if (bitDepth == 2 && planes == 1) {
224 for (int x = 0; x < src.getWidth(); x++) {
225 final int argb = src.getRGB(x, y);
226 final int index = palette.getPaletteIndex(0xffffff & argb);
227 plane0[x >>> 2] |= index << 2 * (3 - (x & 3));
228 }
229 } else if (bitDepth == 4 && planes == 1) {
230 for (int x = 0; x < src.getWidth(); x++) {
231 final int argb = src.getRGB(x, y);
232 final int index = palette.getPaletteIndex(0xffffff & argb);
233 plane0[x >>> 1] |= index << 4 * (1 - (x & 1));
234 }
235 } else if (bitDepth == 8 && planes == 1) {
236 for (int x = 0; x < src.getWidth(); x++) {
237 final int argb = src.getRGB(x, y);
238 final int index = palette.getPaletteIndex(0xffffff & argb);
239 plane0[x] = (byte) index;
240 }
241 } else if (bitDepth == 8 && planes == 3) {
242 for (int x = 0; x < src.getWidth(); x++) {
243 final int argb = src.getRGB(x, y);
244 plane0[x] = (byte) (argb >>> 16);
245 plane1[x] = (byte) (argb >>> 8);
246 plane2[x] = (byte) argb;
247 }
248 }
249
250 for (int i = 0; i < planes; i++) {
251 rleWriter.write(bos, allPlanes[i]);
252 }
253 }
254 rleWriter.flush(bos);
255 }
256
257 private void writePixels32(final BufferedImage src, final int bytesPerLine, final BinaryOutputStream bos) throws IOException {
258
259 final int[] rgbs = Allocator.intArray(src.getWidth());
260 final byte[] plane = Allocator.byteArray(4 * bytesPerLine);
261 for (int y = 0; y < src.getHeight(); y++) {
262 src.getRGB(0, y, src.getWidth(), 1, rgbs, 0, src.getWidth());
263 for (int x = 0; x < rgbs.length; x++) {
264 plane[4 * x + 0] = (byte) rgbs[x];
265 plane[4 * x + 1] = (byte) (rgbs[x] >> 8);
266 plane[4 * x + 2] = (byte) (rgbs[x] >> 16);
267 plane[4 * x + 3] = 0;
268 }
269 rleWriter.write(bos, plane);
270 }
271 rleWriter.flush(bos);
272 }
273 }