/src/csharp/Plupload/FJCore/Decoder/JpegFrame.cs
C# | 283 lines | 193 code | 60 blank | 30 comment | 59 complexity | 4539f9c9de1a220b93aa5bb9d55673b0 MD5 | raw file
1/// Copyright (c) 2008 Jeffrey Powers for Fluxcapacity Open Source.
2/// Under the MIT License, details: License.txt.
3
4using System;
5using System.Collections.Generic;
6using System.Linq;
7using System.Text;
8using FluxJpeg.Core.IO;
9
10namespace FluxJpeg.Core.Decoder
11{
12 internal class JPEGFrame
13 {
14 public static byte JPEG_COLOR_GRAY = 1;
15 public static byte JPEG_COLOR_RGB = 2;
16 public static byte JPEG_COLOR_YCbCr = 3;
17 public static byte JPEG_COLOR_CMYK = 4;
18
19 public byte precision = 8;
20 public byte colorMode = JPEGFrame.JPEG_COLOR_YCbCr;
21
22 public ushort Width { get; private set; }
23 public ushort Height { get; private set; }
24
25 public JpegScan Scan = new JpegScan();
26
27 public Action<long> ProgressUpdateMethod = null;
28
29 public void AddComponent(byte componentID, byte sampleHFactor, byte sampleVFactor,
30 byte quantizationTableID)
31 {
32 Scan.AddComponent(componentID, sampleHFactor, sampleVFactor, quantizationTableID, colorMode);
33 }
34
35 public void setPrecision(byte data) { precision = data; }
36
37 public ushort ScanLines { set { Height = value; } }
38 public ushort SamplesPerLine { set { Width = value; } }
39
40 public byte ColorMode { get {
41 return ComponentCount == 1 ?
42 JPEGFrame.JPEG_COLOR_GRAY :
43 JPEGFrame.JPEG_COLOR_YCbCr;
44
45 }
46 }
47
48 public byte ComponentCount { get ; set; }
49
50 public void setHuffmanTables(byte componentID, JpegHuffmanTable ACTable, JpegHuffmanTable DCTable)
51 {
52 JpegComponent comp = Scan.GetComponentById(componentID);
53
54 if(DCTable != null) comp.setDCTable(DCTable);
55 if(ACTable != null) comp.setACTable(ACTable);
56 }
57
58 public void DecodeScanBaseline(byte numberOfComponents, byte[] componentSelector, int resetInterval, JPEGBinaryReader jpegReader, ref byte marker)
59 {
60 // Set the decode function for all the components
61 for (int compIndex = 0; compIndex < numberOfComponents; compIndex++)
62 {
63 JpegComponent comp = Scan.GetComponentById(componentSelector[compIndex]);
64 comp.Decode = comp.DecodeBaseline;
65 }
66
67 DecodeScan(numberOfComponents, componentSelector, resetInterval, jpegReader, ref marker);
68 }
69
70 private int mcus_per_row(JpegComponent c)
71 {
72 return (((( Width * c.factorH ) + ( Scan.MaxH - 1)) / Scan.MaxH) + 7) / 8;
73 }
74
75 private void DecodeScan(byte numberOfComponents, byte[] componentSelector, int resetInterval, JPEGBinaryReader jpegReader, ref byte marker)
76 {
77 //TODO: not necessary
78 jpegReader.eob_run = 0;
79
80 int mcuIndex = 0;
81 int mcuTotalIndex = 0;
82
83 // This loops through until a MarkerTagFound exception is
84 // found, if the marker tag is a RST (Restart Marker) it
85 // simply skips it and moves on this system does not handle
86 // corrupt data streams very well, it could be improved by
87 // handling misplaced restart markers.
88
89 int h = 0, v = 0;
90 int x = 0;
91
92 long lastPosition = jpegReader.BaseStream.Position;
93
94 //TODO: replace this with a loop which knows how much data to expect
95 while (true)
96 {
97 #region Inform caller of decode progress
98
99 if (ProgressUpdateMethod != null)
100 {
101 if (jpegReader.BaseStream.Position >= lastPosition + JpegDecoder.ProgressUpdateByteInterval)
102 {
103 lastPosition = jpegReader.BaseStream.Position;
104 ProgressUpdateMethod(lastPosition);
105 }
106 }
107
108 #endregion
109
110 try
111 {
112 // Loop though capturing MCU, instruct each
113 // component to read in its necessary count, for
114 // scaling factors the components automatically
115 // read in how much they need
116
117 // Sec A.2.2 from CCITT Rec. T.81 (1992 E)
118 bool interleaved = !(numberOfComponents == 1);
119
120 if (!interleaved)
121 {
122 JpegComponent comp = Scan.GetComponentById(componentSelector[0]);
123
124 comp.SetBlock(mcuIndex);
125
126 comp.DecodeMCU(jpegReader, h, v);
127
128 int mcus_per_line = mcus_per_row(comp);
129 int blocks_per_line = (int) Math.Ceiling((double)this.Width / (8 * comp.factorH));
130
131
132 // TODO: Explain the non-interleaved scan ------
133
134 h++; x++;
135
136 if (h == comp.factorH)
137 {
138 h = 0; mcuIndex++;
139 }
140
141 if( (x % mcus_per_line) == 0)
142 {
143 x = 0;
144 v++;
145
146 if (v == comp.factorV)
147 {
148 if (h != 0) { mcuIndex++; h = 0; }
149 v = 0;
150 }
151 else
152 {
153 mcuIndex -= blocks_per_line;
154
155 // we were mid-block
156 if (h != 0) { mcuIndex++; h = 0; }
157 }
158 }
159
160 // -----------------------------------------------
161
162 }
163 else // Components are interleaved
164 {
165 for (int compIndex = 0; compIndex < numberOfComponents; compIndex++)
166 {
167 JpegComponent comp = Scan.GetComponentById(componentSelector[compIndex]);
168 comp.SetBlock(mcuTotalIndex);
169
170 for (int j = 0; j < comp.factorV; j++)
171 for (int i = 0; i < comp.factorH; i++)
172 {
173 comp.DecodeMCU(jpegReader, i, j);
174 }
175 }
176
177 mcuIndex++;
178 mcuTotalIndex++;
179 }
180 }
181 // We've found a marker, see if the marker is a restart
182 // marker or just the next marker in the stream. If
183 // it's the next marker in the stream break out of the
184 // while loop, if it's just a restart marker skip it
185 catch (JPEGMarkerFoundException ex)
186 {
187 marker = ex.Marker;
188
189 // Handle JPEG Restart Markers, this is where the
190 // count of MCU's per interval is compared with
191 // the count actually obtained, if it's short then
192 // pad on some MCU's ONLY for components that are
193 // greater than one. Also restart the DC prediction
194 // to zero.
195 if (marker == JPEGMarker.RST0
196 || marker == JPEGMarker.RST1
197 || marker == JPEGMarker.RST2
198 || marker == JPEGMarker.RST3
199 || marker == JPEGMarker.RST4
200 || marker == JPEGMarker.RST5
201 || marker == JPEGMarker.RST6
202 || marker == JPEGMarker.RST7)
203 {
204 for (int compIndex = 0; compIndex < numberOfComponents; compIndex++)
205 {
206 JpegComponent comp = Scan.GetComponentById(componentSelector[compIndex]);
207 if (compIndex > 1)
208 comp.padMCU(mcuTotalIndex, resetInterval - mcuIndex);
209 comp.resetInterval();
210 }
211
212 mcuTotalIndex += (resetInterval - mcuIndex);
213 mcuIndex = 0;
214 }
215 else
216 {
217 break; // We're at the end of our scan, exit out.
218 }
219 }
220 }
221
222 }
223
224 public void DecodeScanProgressive(byte successiveApproximation, byte startSpectralSelection, byte endSpectralSelection,
225 byte numberOfComponents, byte[] componentSelector, int resetInterval, JPEGBinaryReader jpegReader, ref byte marker)
226 {
227
228 byte successiveHigh = (byte)(successiveApproximation >> 4);
229 byte successiveLow = (byte)(successiveApproximation & 0x0f);
230
231 if ((startSpectralSelection > endSpectralSelection) || (endSpectralSelection > 63))
232 throw new Exception("Bad spectral selection.");
233
234 bool dcOnly = startSpectralSelection == 0;
235 bool refinementScan = (successiveHigh != 0);
236
237 if (dcOnly) // DC scan
238 {
239 if (endSpectralSelection != 0)
240 throw new Exception("Bad spectral selection for DC only scan.");
241 }
242 else // AC scan
243 {
244 if (numberOfComponents > 1)
245 throw new Exception("Too many components for AC scan!");
246 }
247
248 // Set the decode function for all the components
249 // TODO: set this for the scan and let the component figure it out
250 for (int compIndex = 0; compIndex < numberOfComponents; compIndex++)
251 {
252 JpegComponent comp = Scan.GetComponentById(componentSelector[compIndex]);
253
254 comp.successiveLow = successiveLow;
255
256 if (dcOnly)
257 {
258 if (refinementScan) // DC refine
259 comp.Decode = comp.DecodeDCRefine;
260 else // DC first
261 comp.Decode = comp.DecodeDCFirst;
262 }
263 else
264 {
265 comp.spectralStart = startSpectralSelection;
266 comp.spectralEnd = endSpectralSelection;
267
268 if (refinementScan) // AC refine
269 comp.Decode = comp.DecodeACRefine;
270 else // AC first
271 comp.Decode = comp.DecodeACFirst;
272 }
273 }
274
275 DecodeScan(numberOfComponents, componentSelector, resetInterval, jpegReader, ref marker);
276
277 }
278
279
280
281 }
282
283}