1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.imaging.formats.png;
18
19 import static org.apache.commons.imaging.common.BinaryFunctions.readBytes;
20
21 import java.awt.image.BufferedImage;
22 import java.io.IOException;
23 import java.io.InputStream;
24
25 import org.apache.commons.imaging.ImagingException;
26 import org.apache.commons.imaging.common.Allocator;
27 import org.apache.commons.imaging.formats.png.chunks.PngChunkPlte;
28 import org.apache.commons.imaging.formats.png.scanlinefilters.ScanlineFilter;
29 import org.apache.commons.imaging.formats.png.scanlinefilters.ScanlineFilterAverage;
30 import org.apache.commons.imaging.formats.png.scanlinefilters.ScanlineFilterNone;
31 import org.apache.commons.imaging.formats.png.scanlinefilters.ScanlineFilterPaeth;
32 import org.apache.commons.imaging.formats.png.scanlinefilters.ScanlineFilterSub;
33 import org.apache.commons.imaging.formats.png.scanlinefilters.ScanlineFilterUp;
34 import org.apache.commons.imaging.formats.png.transparencyfilters.AbstractTransparencyFilter;
35
36 abstract class AbstractScanExpediter {
37
38 final int width;
39 final int height;
40 final InputStream is;
41 final BufferedImage bi;
42 final PngColorType pngColorType;
43 final int bitDepth;
44 final int bytesPerPixel;
45 final int bitsPerPixel;
46 final PngChunkPlte pngChunkPlte;
47 final GammaCorrection gammaCorrection;
48 final AbstractTransparencyFilter abstractTransparencyFilter;
49
50 AbstractScanExpediter(final int width, final int height, final InputStream is, final BufferedImage bi, final PngColorType pngColorType, final int bitDepth,
51 final int bitsPerPixel, final PngChunkPlte pngChunkPLTE, final GammaCorrection gammaCorrection,
52 final AbstractTransparencyFilter abstractTransparencyFilter) {
53 this.width = width;
54 this.height = height;
55 this.is = is;
56 this.bi = bi;
57 this.pngColorType = pngColorType;
58 this.bitDepth = bitDepth;
59 this.bytesPerPixel = this.getBitsToBytesRoundingUp(bitsPerPixel);
60 this.bitsPerPixel = bitsPerPixel;
61 this.pngChunkPlte = pngChunkPLTE;
62 this.gammaCorrection = gammaCorrection;
63 this.abstractTransparencyFilter = abstractTransparencyFilter;
64 }
65
66 public abstract void drive() throws ImagingException, IOException;
67
68 final int getBitsToBytesRoundingUp(final int bits) {
69 return (bits + 7) / 8;
70 }
71
72 byte[] getNextScanline(final InputStream is, final int length, final byte[] prev, final int bytesPerPixel) throws ImagingException, IOException {
73 final int filterType = is.read();
74 if (filterType < 0) {
75 throw new ImagingException("PNG: missing filter type");
76 }
77 if (filterType >= FilterType.values().length) {
78 throw new ImagingException("PNG: unknown filterType: " + filterType);
79 }
80
81 final byte[] scanline = readBytes("scanline", is, length, "PNG: missing image data");
82
83 return unfilterScanline(FilterType.values()[filterType], scanline, prev, bytesPerPixel);
84 }
85
86 final int getPixelArgb(final int alpha, final int red, final int green, final int blue) {
87 return (0xff & alpha) << 24 | (0xff & red) << 16 | (0xff & green) << 8 | (0xff & blue) << 0;
88 }
89
90 final int getPixelRgb(final int red, final int green, final int blue) {
91 return getPixelArgb(0xff, red, green, blue);
92 }
93
94 int getRgb(final BitParser bitParser, final int pixelIndexInScanline) throws ImagingException, IOException {
95
96 switch (pngColorType) {
97 case GREYSCALE: {
98
99 int sample = bitParser.getSampleAsByte(pixelIndexInScanline, 0);
100
101 if (gammaCorrection != null) {
102 sample = gammaCorrection.correctSample(sample);
103 }
104
105 int rgb = getPixelRgb(sample, sample, sample);
106
107 if (abstractTransparencyFilter != null) {
108 rgb = abstractTransparencyFilter.filter(rgb, sample);
109 }
110
111 return rgb;
112
113 }
114 case TRUE_COLOR: {
115
116 int red = bitParser.getSampleAsByte(pixelIndexInScanline, 0);
117 int green = bitParser.getSampleAsByte(pixelIndexInScanline, 1);
118 int blue = bitParser.getSampleAsByte(pixelIndexInScanline, 2);
119
120 int rgb = getPixelRgb(red, green, blue);
121
122 if (abstractTransparencyFilter != null) {
123 rgb = abstractTransparencyFilter.filter(rgb, -1);
124 }
125
126 if (gammaCorrection != null) {
127 final int alpha = (0xff000000 & rgb) >> 24;
128
129 red = gammaCorrection.correctSample(red);
130 green = gammaCorrection.correctSample(green);
131 blue = gammaCorrection.correctSample(blue);
132 rgb = getPixelArgb(alpha, red, green, blue);
133 }
134
135 return rgb;
136 }
137
138 case INDEXED_COLOR: {
139
140
141 if (pngChunkPlte == null) {
142 throw new ImagingException("A PLTE chunk is required for an indexed color type.");
143 }
144 final int index = bitParser.getSample(pixelIndexInScanline, 0);
145
146 int rgb = pngChunkPlte.getRgb(index);
147
148 if (abstractTransparencyFilter != null) {
149 rgb = abstractTransparencyFilter.filter(rgb, index);
150 }
151
152 return rgb;
153 }
154 case GREYSCALE_WITH_ALPHA: {
155
156
157 int sample = bitParser.getSampleAsByte(pixelIndexInScanline, 0);
158 final int alpha = bitParser.getSampleAsByte(pixelIndexInScanline, 1);
159
160 if (gammaCorrection != null) {
161 sample = gammaCorrection.correctSample(sample);
162 }
163
164 return getPixelArgb(alpha, sample, sample, sample);
165 }
166 case TRUE_COLOR_WITH_ALPHA: {
167
168 int red = bitParser.getSampleAsByte(pixelIndexInScanline, 0);
169 int green = bitParser.getSampleAsByte(pixelIndexInScanline, 1);
170 int blue = bitParser.getSampleAsByte(pixelIndexInScanline, 2);
171 final int alpha = bitParser.getSampleAsByte(pixelIndexInScanline, 3);
172
173 if (gammaCorrection != null) {
174 red = gammaCorrection.correctSample(red);
175 green = gammaCorrection.correctSample(green);
176 blue = gammaCorrection.correctSample(blue);
177 }
178
179 return getPixelArgb(alpha, red, green, blue);
180 }
181 default:
182 throw new ImagingException("PNG: unknown color type: " + pngColorType);
183 }
184 }
185
186 ScanlineFilter getScanlineFilter(final FilterType filterType, final int bytesPerPixel) {
187 switch (filterType) {
188 case NONE:
189 return new ScanlineFilterNone();
190 case SUB:
191 return new ScanlineFilterSub(bytesPerPixel);
192 case UP:
193 return new ScanlineFilterUp();
194 case AVERAGE:
195 return new ScanlineFilterAverage(bytesPerPixel);
196 case PAETH:
197 return new ScanlineFilterPaeth(bytesPerPixel);
198 }
199
200 return null;
201 }
202
203 byte[] unfilterScanline(final FilterType filterType, final byte[] src, final byte[] prev, final int bytesPerPixel) throws ImagingException, IOException {
204 final ScanlineFilter filter = getScanlineFilter(filterType, bytesPerPixel);
205
206 final byte[] dst = Allocator.byteArray(src.length);
207 filter.unfilter(src, dst, prev);
208 return dst;
209 }
210
211 }