// 调整图片文件大小 private void ResizeImage() { UploadStatus = UploadStatus.Resizing; using (Stream fileStream = m_file.OpenRead()) { // 利用FluxJpeg解码 DecodedJpeg jpegIn = new JpegDecoder(fileStream).Decode(); if (ImageResizer.ResizeNeeded(jpegIn.Image, ImageSize)) { // 调整 DecodedJpeg jpegOut = new DecodedJpeg( new ImageResizer(jpegIn.Image).Resize(ImageSize, ResamplingFilters.NearestNeighbor) , jpegIn.MetaHeaders); // 获取EXIF详细信息 // 编码 m_resizeStream = new MemoryStream(); new JpegEncoder(jpegOut, 90, m_resizeStream).Encode(); // 显示 m_resizeStream.Seek(0, SeekOrigin.Begin); FileLength = m_resizeStream.Length; } } }
void Resize() { Status = FileUploadStatus.Resizing; try { Stream fileStream = file.OpenRead(); using (fileStream) { // Decode DecodedJpeg jpegIn = new JpegDecoder(fileStream).Decode(); if (!ImageResizer.ResizeNeeded(jpegIn.Image, ImageSize)) { return; } else { // Resize DecodedJpeg jpegOut = new DecodedJpeg( new ImageResizer(jpegIn.Image) .Resize(ImageSize, ResamplingFilters.NearestNeighbor), jpegIn.MetaHeaders); // Retain EXIF details // Encode resizeStream = new MemoryStream(); new JpegEncoder(jpegOut, 90, resizeStream).Encode(); // Display resizeStream.Seek(0, SeekOrigin.Begin); FileLength = resizeStream.Length; } } } catch { }; }
public void JpegEncoderDecoderTest() { // Encode and decode a basic raster structure. var colorModel = new ColorModel(); colorModel.ColorSpace = ColorSpace.YCbCr; colorModel.Opaque = true; byte[][][] originalRaster = GetRaster(); var image = new Image(colorModel, originalRaster); var stream = new MemoryStream(); var encoder = new JpegEncoder(image, 50, stream); encoder.Encode(); stream.Seek(0, SeekOrigin.Begin); var decoder = new JpegDecoder(stream); DecodedJpeg decodedImage = decoder.Decode(); // Check that the returned raster structure looks something like what we passed in. for (int i = 0; i < 3; i++) { for (int j = 0; j < width; j++) { for (int k = 0; k < height; k++) { // Tune this. int diff = Math.Abs(decodedImage.Image.Raster[i][j][k] - originalRaster[i][j][k]); Assert.IsTrue(diff < 5); } } } ClientLogger.Debug("Finished JpegEncoderDecoder test."); }
static Image Resize(string pathIn, int edge) { JpegDecoder decoder = new JpegDecoder(File.Open(pathIn, FileMode.Open)); DecodedJpeg jpeg = decoder.Decode(); ImageResizer resizer = new ImageResizer(jpeg.Image); return(resizer.ResizeToScale(edge, ResamplingFilters.LowpassAntiAlias)); }
public JpegEncoder(DecodedJpeg decodedJpeg, int quality, Stream outStream) { this._input = decodedJpeg; this._input.Image.ChangeColorSpace(ColorSpace.YCbCr); this._quality = quality; this._height = this._input.Image.Height; this._width = this._input.Image.Width; this._outStream = outStream; this._dct = new DCT(this._quality); this._huf = new HuffmanTable(null); }
public JpegEncoder(DecodedJpeg decodedJpeg, int quality, Stream outStream) { _input = decodedJpeg; _input.Image.ChangeColorSpace(ColorSpace.YCbCr); _quality = quality; _height = _input.Image.Height; _width = _input.Image.Width; _outStream = outStream; _dct = new DCT(_quality); _huf = new HuffmanTable(null); }
private void Button_Click(object sender, RoutedEventArgs e) { OpenFileDialog ofd = new OpenFileDialog() { Filter = "Image files (*.jpg)|*.jpg" }; if (ofd.ShowDialog() != true) { return; } Stream fileStream = ofd.File.OpenRead(); // Display input image Stream inStream = new MemoryStream(new BinaryReader(fileStream).ReadBytes((int)fileStream.Length)); BitmapImage imageIn = new BitmapImage(); imageIn.SetSource(inStream); InputImage.Source = imageIn; // Rewind fileStream.Seek(0, SeekOrigin.Begin); using (fileStream) { // Decode DecodedJpeg jpegIn = new JpegDecoder(fileStream).Decode(); if (!ImageResizer.ResizeNeeded(jpegIn.Image, 320)) { OutputImage.Source = null; OutputText.Text = "No resize necessary."; return; } // Resize DecodedJpeg jpegOut = new DecodedJpeg( new ImageResizer(jpegIn.Image) .Resize(320, ResamplingFilters.NearestNeighbor), jpegIn.MetaHeaders); // Retain EXIF details // Encode MemoryStream outStream = new MemoryStream(); new JpegEncoder(jpegOut, 90, outStream).Encode(); // Display outStream.Seek(0, SeekOrigin.Begin); BitmapImage image = new BitmapImage(); image.SetSource(outStream); OutputImage.Source = image; } }
/// <summary> /// Encodes the data of the specified image and writes the result to /// the specified stream. /// </summary> /// <param name="image">The image, where the data should be get from. /// Cannot be null (Nothing in Visual Basic).</param> /// <param name="stream">The stream, where the image data should be written to. /// Cannot be null (Nothing in Visual Basic).</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="image"/> is null (Nothing in Visual Basic).</para> /// <para>- or -</para> /// <para><paramref name="stream"/> is null (Nothing in Visual Basic).</para> /// </exception> public void Encode(ExtendedImage image, Stream stream) { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); const int bands = 3; int pixelWidth = image.PixelWidth; int pixelHeight = image.PixelHeight; byte[] sourcePixels = image.Pixels; byte[][,] pixels = new byte[bands][, ]; for (int b = 0; b < bands; b++) { pixels[b] = new byte[pixelWidth, pixelHeight]; } for (int y = 0; y < pixelHeight; y++) { for (int x = 0; x < pixelWidth; x++) { int offset = (y * pixelWidth + x) * 4; float a = (float)sourcePixels[offset + 3] / 255.0f; pixels[0][x, y] = (byte)(sourcePixels[offset + 0] * a + (1 - a) * _transparentColor.R); pixels[1][x, y] = (byte)(sourcePixels[offset + 1] * a + (1 - a) * _transparentColor.G); pixels[2][x, y] = (byte)(sourcePixels[offset + 2] * a + (1 - a) * _transparentColor.B); } } Image newImage = new Image(new ColorModel { ColorSpace = ColorSpace.RGB, Opaque = false }, pixels); if (image.DensityX > 0 && image.DensityY > 0) { newImage.DensityX = image.DensityX; newImage.DensityY = image.DensityY; } // Create a jpg image from the image object. DecodedJpeg jpg = new DecodedJpeg(newImage); // Create a new encoder and start encoding. FluxCoreJpegEncoder fluxCoreJpegEncoder = new FluxCoreJpegEncoder(jpg, _quality, stream); fluxCoreJpegEncoder.Encode(); }
/// <summary> /// Encodes a JPEG, preserving the colorspace and metadata of the input JPEG. /// </summary> /// <param name="decodedJpeg">Decoded Jpeg to start with.</param> /// <param name="quality">Quality of the image from 0 to 100. (Compression from max to min.)</param> /// <param name="outStream">Stream where the result will be placed.</param> public JpegEncoder(DecodedJpeg decodedJpeg, int quality, Stream outStream) { _input = decodedJpeg; /* This encoder requires YCbCr */ _input.Image.ChangeColorSpace(ColorSpace.YCbCr); _quality = quality; _height = _input.Image.Height; _width = _input.Image.Width; _outStream = outStream; _dct = new DCT(_quality); _huf = new HuffmanTable(null); }
/// <summary> /// Encodes a JPEG, preserving the colorspace and metadata of the input JPEG. /// </summary> /// <param name="decodedJpeg">Decoded Jpeg to start with.</param> /// <param name="quality">Quality of the image from 0 to 100. (Compression from max to min.)</param> /// <param name="outStream">Stream where the result will be placed.</param> public JpegEncoder(DecodedJpeg decodedJpeg, short quality, Stream outStream) { input = decodedJpeg; /* This encoder requires YCbCr */ input.Image.ChangeColorSpace(ColorSpace.YCbCr); this.quality = quality; height = input.Image.Height; width = input.Image.Width; this.outStream = outStream; dct = new DCT(quality); huf = HuffmanTable.GetHuffmanTable(null); }
/// <summary> /// Decodes the image from the specified stream and sets /// the data to image. /// </summary> /// <param name="image">The image, where the data should be set to. /// Cannot be null (Nothing in Visual Basic).</param> /// <param name="stream">The stream, where the image should be /// decoded from. Cannot be null (Nothing in Visual Basic).</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="image"/> is null (Nothing in Visual Basic).</para> /// <para>- or -</para> /// <para><paramref name="stream"/> is null (Nothing in Visual Basic).</para> /// </exception> public void Decode(ExtendedImage image, Stream stream) { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); FluxCoreJpegDecoder fluxCoreJpegDecoder = new FluxCoreJpegDecoder(stream); DecodedJpeg jpg = fluxCoreJpegDecoder.Decode(); jpg.Image.ChangeColorSpace(ColorSpace.RGB); int pixelWidth = jpg.Image.Width; int pixelHeight = jpg.Image.Height; byte[] pixels = new byte[pixelWidth * pixelHeight * 4]; byte[][,] sourcePixels = jpg.Image.Raster; for (int y = 0; y < pixelHeight; y++) { for (int x = 0; x < pixelWidth; x++) { int offset = (y * pixelWidth + x) * 4; pixels[offset + 0] = sourcePixels[0][x, y]; pixels[offset + 1] = sourcePixels[1][x, y]; pixels[offset + 2] = sourcePixels[2][x, y]; pixels[offset + 3] = (byte)255; } } image.DensityX = jpg.Image.DensityX; image.DensityY = jpg.Image.DensityY; image.SetPixels(pixelWidth, pixelHeight, pixels); }
public DecodedJpeg Decode() { // The frames in this jpeg are loaded into a list. There is // usually just one frame except in heirarchial progression where // there are multiple frames. JPEGFrame frame = null; // The restart interval defines how many MCU's we should have // between the 8-modulo restart marker. The restart markers allow // us to tell whether or not our decoding process is working // correctly, also if there is corruption in the image we can // recover with these restart intervals. (See RSTm DRI). int resetInterval = 0; bool haveMarker = false; bool foundJFIF = false; List<JpegHeader> headers = new List<JpegHeader>(); // Loop through until there are no more markers to read in, at // that point everything is loaded into the jpegFrames array and // can be processed. while (true) { if (DecodeProgress.Abort) return null; #region Switch over marker types switch (marker) { case JPEGMarker.APP0: // APP1 is used for EXIF data case JPEGMarker.APP1: // Seldomly, APP2 gets used for extended EXIF, too case JPEGMarker.APP2: case JPEGMarker.APP3: case JPEGMarker.APP4: case JPEGMarker.APP5: case JPEGMarker.APP6: case JPEGMarker.APP7: case JPEGMarker.APP8: case JPEGMarker.APP9: case JPEGMarker.APP10: case JPEGMarker.APP11: case JPEGMarker.APP12: case JPEGMarker.APP13: case JPEGMarker.APP14: case JPEGMarker.APP15: // COM: Comment case JPEGMarker.COM: // Debug.WriteLine(string.Format("Extracting Header, Type={0:X}", marker)); JpegHeader header = ExtractHeader(); #region Check explicitly for Exif Data if (header.Marker == JPEGMarker.APP1 && header.Data.Length >= 6) { byte[] d = header.Data; if( d[0] == 'E' && d[1] == 'x' && d[2] == 'i' && d[3] == 'f' && d[4] == 0 && d[5] == 0) { // Exif. Do something? } } #endregion #region Check for Adobe header if (header.Data.Length >= 5 && header.Marker == JPEGMarker.APP14) { string asText = UTF8Encoding.UTF8.GetString(header.Data, 0, 5); if (asText == "Adobe") { // ADOBE HEADER. Do anything? } } #endregion headers.Add(header); if (!foundJFIF && marker == JPEGMarker.APP0) { foundJFIF = TryParseJFIF(header.Data); if (foundJFIF) // Found JFIF... do JFIF extension follow? { header.IsJFIF = true; marker = jpegReader.GetNextMarker(); // Yes, they do. if (marker == JPEGMarker.APP0) { header = ExtractHeader(); headers.Add(header); } else // No. Delay processing this one. haveMarker = true; } } break; case JPEGMarker.SOF0: case JPEGMarker.SOF2: // SOFn Start of Frame Marker, Baseline DCT - This is the start // of the frame header that defines certain variables that will // be carried out through the rest of the encoding. Multiple // frames are used in a hierarchical system, however most JPEG's // only contain a single frame. // Progressive or baseline? progressive = marker == JPEGMarker.SOF2; jpegFrames.Add(new JPEGFrame()); frame = (JPEGFrame)jpegFrames[jpegFrames.Count - 1]; frame.ProgressUpdateMethod = new Action<long>(UpdateStreamProgress); // Skip the frame length. jpegReader.ReadShort(); // Bits percision, either 8 or 12. frame.setPrecision(jpegReader.ReadByte()); // Scan lines (height) frame.ScanLines = jpegReader.ReadShort(); // Scan samples per line (width) frame.SamplesPerLine = jpegReader.ReadShort(); // Number of Color Components (channels). frame.ComponentCount = jpegReader.ReadByte(); DecodeProgress.Height = frame.Height; DecodeProgress.Width = frame.Width; DecodeProgress.SizeReady = true; if(DecodeProgressChanged != null) { DecodeProgressChanged(this, DecodeProgress); if (DecodeProgress.Abort) return null; } // Add all of the necessary components to the frame. for (int i = 0; i < frame.ComponentCount; i++) { byte compId = jpegReader.ReadByte(); byte sampleFactors = jpegReader.ReadByte(); byte qTableId = jpegReader.ReadByte(); byte sampleHFactor = (byte)(sampleFactors >> 4); byte sampleVFactor = (byte)(sampleFactors & 0x0f); frame.AddComponent(compId, sampleHFactor, sampleVFactor, qTableId); } break; case JPEGMarker.DHT: // DHT non-SOF Marker - Huffman Table is required for decoding // the JPEG stream, when we receive a marker we load in first // the table length (16 bits), the table class (4 bits), table // identifier (4 bits), then we load in 16 bytes and each byte // represents the count of bytes to load in for each of the 16 // bytes. We load this into an array to use later and move on 4 // huffman tables can only be used in an image. int huffmanLength = (jpegReader.ReadShort() - 2); // Keep looping until we are out of length. int index = huffmanLength; // Multiple tables may be defined within a DHT marker. This // will keep reading until there are no tables left, most // of the time there are just one tables. while (index > 0) { // Read the identifier information and class // information about the Huffman table, then read the // 16 byte codelength in and read in the Huffman values // and put it into table info. byte huffmanInfo = jpegReader.ReadByte(); byte tableClass = (byte)(huffmanInfo >> 4); byte huffmanIndex = (byte)(huffmanInfo & 0x0f); short[] codeLength = new short[16]; for (int i = 0; i < codeLength.Length; i++) codeLength[i] = jpegReader.ReadByte(); int huffmanValueLen = 0; for (int i = 0; i < 16; i++) huffmanValueLen += codeLength[i]; index -= (huffmanValueLen + 17); short[] huffmanVal = new short[huffmanValueLen]; for (int i = 0; i < huffmanVal.Length; i++) { huffmanVal[i] = jpegReader.ReadByte(); } // Assign DC Huffman Table. if (tableClass == HuffmanTable.JPEG_DC_TABLE) dcTables[(int)huffmanIndex] = new JpegHuffmanTable(codeLength, huffmanVal); // Assign AC Huffman Table. else if (tableClass == HuffmanTable.JPEG_AC_TABLE) acTables[(int)huffmanIndex] = new JpegHuffmanTable(codeLength, huffmanVal); } break; case JPEGMarker.DQT: // DQT non-SOF Marker - This defines the quantization // coeffecients, this allows us to figure out the quality of // compression and unencode the data. The data is loaded and // then stored in to an array. short quantizationLength = (short)(jpegReader.ReadShort() - 2); for (int j = 0; j < quantizationLength / 65; j++) { byte quantSpecs = jpegReader.ReadByte(); int[] quantData = new int[64]; if ((byte)(quantSpecs >> 4) == 0) // Precision 8 bit. { for (int i = 0; i < 64; i++) quantData[i] = jpegReader.ReadByte(); } else if ((byte)(quantSpecs >> 4) == 1) // Precision 16 bit. { for (int i = 0; i < 64; i++) quantData[i] = jpegReader.ReadShort(); } qTables[(int)(quantSpecs & 0x0f)] = new JpegQuantizationTable(quantData); } break; case JPEGMarker.SOS: Debug.WriteLine("Start of Scan (SOS)"); // SOS non-SOF Marker - Start Of Scan Marker, this is where the // actual data is stored in a interlaced or non-interlaced with // from 1-4 components of color data, if three components most // likely a YCrCb model, this is a fairly complex process. // Read in the scan length. ushort scanLen = jpegReader.ReadShort(); // Number of components in the scan. byte numberOfComponents = jpegReader.ReadByte(); byte[] componentSelector = new byte[numberOfComponents]; for (int i = 0; i < numberOfComponents; i++) { // Component ID, packed byte containing the Id for the // AC table and DC table. byte componentID = jpegReader.ReadByte(); byte tableInfo = jpegReader.ReadByte(); int DC = (tableInfo >> 4) & 0x0f; int AC = (tableInfo) & 0x0f; frame.setHuffmanTables(componentID, acTables[(byte)AC], dcTables[(byte)DC]); componentSelector[i] = componentID; } byte startSpectralSelection = jpegReader.ReadByte(); byte endSpectralSelection = jpegReader.ReadByte(); byte successiveApproximation = jpegReader.ReadByte(); #region Baseline JPEG Scan Decoding if (!progressive) { frame.DecodeScanBaseline(numberOfComponents, componentSelector, resetInterval, jpegReader, ref marker); haveMarker = true; // use resultant marker for the next switch(..) } #endregion #region Progressive JPEG Scan Decoding if (progressive) { frame.DecodeScanProgressive( successiveApproximation, startSpectralSelection, endSpectralSelection, numberOfComponents, componentSelector, resetInterval, jpegReader, ref marker); haveMarker = true; // use resultant marker for the next switch(..) } #endregion break; case JPEGMarker.DRI: jpegReader.BaseStream.Seek(2, System.IO.SeekOrigin.Current); resetInterval = jpegReader.ReadShort(); break; /// Defines the number of lines. (Not usually present) case JPEGMarker.DNL: frame.ScanLines = jpegReader.ReadShort(); break; /// End of Image. Finish the decode. case JPEGMarker.EOI: if (jpegFrames.Count == 0) { throw new NotSupportedException("No JPEG frames could be located."); } else if (jpegFrames.Count == 1) { // Only one frame, JPEG Non-Heirarchial Frame. byte[][,] raster = Image.CreateRaster(frame.Width, frame.Height, frame.ComponentCount); IList<JpegComponent> components = frame.Scan.Components; int totalSteps = components.Count * 3; // Three steps per loop int stepsFinished = 0; for(int i = 0; i < components.Count; i++) { JpegComponent comp = components[i]; comp.QuantizationTable = qTables[comp.quant_id].Table; // 1. Quantize comp.quantizeData(); UpdateProgress(++stepsFinished, totalSteps); // 2. Run iDCT (expensive) comp.idctData(); UpdateProgress(++stepsFinished, totalSteps); // 3. Scale the image and write the data to the raster. comp.writeDataScaled(raster, i, BlockUpsamplingMode); UpdateProgress(++stepsFinished, totalSteps); // Ensure garbage collection. comp = null; GC.Collect(); } // Grayscale Color Image (1 Component). if (frame.ComponentCount == 1) { ColorModel cm = new ColorModel() { colorspace = ColorSpace.Gray, Opaque = true }; image = new Image(cm, raster); } // YCbCr Color Image (3 Components). else if (frame.ComponentCount == 3) { ColorModel cm = new ColorModel() { colorspace = ColorSpace.YCbCr, Opaque = true }; image = new Image(cm, raster); } // Possibly CMYK or RGBA ? else { throw new NotSupportedException("Unsupported Color Mode: 4 Component Color Mode found."); } // If needed, convert centimeters to inches. Func<double, double> conv = x => Units == UnitType.Inches ? x : x / 2.54; image.DensityX = conv(XDensity); image.DensityY = conv(YDensity); height = frame.Height; width = frame.Width; } else { // JPEG Heirarchial Frame throw new NotSupportedException("Unsupported Codec Type: Hierarchial JPEG"); } break; // Only SOF0 (baseline) and SOF2 (progressive) are supported by FJCore case JPEGMarker.SOF1: case JPEGMarker.SOF3: case JPEGMarker.SOF5: case JPEGMarker.SOF6: case JPEGMarker.SOF7: case JPEGMarker.SOF9: case JPEGMarker.SOF10: case JPEGMarker.SOF11: case JPEGMarker.SOF13: case JPEGMarker.SOF14: case JPEGMarker.SOF15: throw new NotSupportedException("Unsupported codec type."); default: break; // ignore } #endregion switch over markers if (haveMarker) haveMarker = false; else { try { marker = jpegReader.GetNextMarker(); } catch (System.IO.EndOfStreamException) { break; /* done reading the file */ } } } DecodedJpeg result = new DecodedJpeg(image, headers); return result; }
/// <summary> /// Decodes the image from the specified stream and sets /// the data to image. /// </summary> /// <param name="image">The image, where the data should be set to. /// Cannot be null (Nothing in Visual Basic).</param> /// <param name="stream">The stream, where the image should be /// decoded from. Cannot be null (Nothing in Visual Basic).</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="image"/> is null (Nothing in Visual Basic).</para> /// <para>- or -</para> /// <para><paramref name="stream"/> is null (Nothing in Visual Basic).</para> /// </exception> public void Decode(ExtendedImage image, Stream stream) { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); if (UseLegacyLibrary) { FluxCoreJpegDecoder fluxCoreJpegDecoder = new FluxCoreJpegDecoder(stream); DecodedJpeg jpg = fluxCoreJpegDecoder.Decode(); jpg.Image.ChangeColorSpace(ColorSpace.RGB); int pixelWidth = jpg.Image.Width; int pixelHeight = jpg.Image.Height; byte[] pixels = new byte[pixelWidth * pixelHeight * 4]; byte[][,] sourcePixels = jpg.Image.Raster; for (int y = 0; y < pixelHeight; y++) { for (int x = 0; x < pixelWidth; x++) { int offset = (y * pixelWidth + x) * 4; pixels[offset + 0] = sourcePixels[0][x, y]; pixels[offset + 1] = sourcePixels[1][x, y]; pixels[offset + 2] = sourcePixels[2][x, y]; pixels[offset + 3] = (byte)255; } } //------- image.DensityXInt32 = jpg.Image.DensityX; image.DensityYInt32 = jpg.Image.DensityY; image.SetPixels(pixelWidth, pixelHeight, pixels); } else { JpegImage jpg = new JpegImage(stream); int pixelWidth = jpg.Width; int pixelHeight = jpg.Height; byte[] pixels = new byte[pixelWidth * pixelHeight * 4]; if (!(jpg.Colorspace == Colorspace.RGB && jpg.BitsPerComponent == 8)) { throw new UnsupportedImageFormatException(); } for (int y = 0; y < pixelHeight; y++) { SampleRow row = jpg.GetRow(y); for (int x = 0; x < pixelWidth; x++) { //Sample sample = row.GetAt(x); int offset = (y * pixelWidth + x) * 4; row.GetComponentsAt(x, out pixels[offset + 0], out pixels[offset + 1], out pixels[offset + 2]); //r = (byte)sample[0]; //g = (byte)sample[1]; //b = (byte)sample[2]; //pixels[offset + 0] = r; //pixels[offset + 1] = g; //pixels[offset + 2] = b; pixels[offset + 3] = (byte)255; } } image.SetPixels(pixelWidth, pixelHeight, pixels); } }
public DecodedJpeg Decode() { // The frames in this jpeg are loaded into a list. There is // usually just one frame except in heirarchial progression where // there are multiple frames. JPEGFrame frame = null; // The restart interval defines how many MCU's we should have // between the 8-modulo restart marker. The restart markers allow // us to tell whether or not our decoding process is working // correctly, also if there is corruption in the image we can // recover with these restart intervals. (See RSTm DRI). int resetInterval = 0; bool haveMarker = false; bool foundJFIF = false; List <JpegHeader> headers = new List <JpegHeader>(); // Loop through until there are no more markers to read in, at // that point everything is loaded into the jpegFrames array and // can be processed. while (true) { #region Switch over marker types switch (marker) { case JPEGMarker.APP0: // APP1 is used for EXIF data case JPEGMarker.APP1: // Seldomly, APP2 gets used for extended EXIF, too case JPEGMarker.APP2: case JPEGMarker.APP3: case JPEGMarker.APP4: case JPEGMarker.APP5: case JPEGMarker.APP6: case JPEGMarker.APP7: case JPEGMarker.APP8: case JPEGMarker.APP9: case JPEGMarker.APP10: case JPEGMarker.APP11: case JPEGMarker.APP12: case JPEGMarker.APP13: case JPEGMarker.APP14: case JPEGMarker.APP15: // COM: Comment case JPEGMarker.COM: // Debug.WriteLine(string.Format("Extracting Header, Type={0:X}", marker)); JpegHeader header = ExtractHeader(); #region Check explicitly for Exif Data if (header.Marker == JPEGMarker.APP1 && header.Data.Length >= 6) { byte[] d = header.Data; if (d[0] == 'E' && d[1] == 'x' && d[2] == 'i' && d[3] == 'f' && d[4] == 0 && d[5] == 0) { // Exif. Do something? } } #endregion #region Check for Adobe header if (header.Data.Length >= 5 && header.Marker == JPEGMarker.APP14) { string asText = UTF8Encoding.UTF8.GetString(header.Data, 0, 5); if (asText == "Adobe") { // ADOBE HEADER. Do anything? } } #endregion headers.Add(header); if (!foundJFIF && marker == JPEGMarker.APP0) { foundJFIF = TryParseJFIF(header.Data); if (foundJFIF) // Found JFIF... do JFIF extension follow? { header.IsJFIF = true; marker = jpegReader.GetNextMarker(); // Yes, they do. if (marker == JPEGMarker.APP0) { header = ExtractHeader(); headers.Add(header); } else // No. Delay processing this one. { haveMarker = true; } } } break; case JPEGMarker.SOF0: case JPEGMarker.SOF2: // SOFn Start of Frame Marker, Baseline DCT - This is the start // of the frame header that defines certain variables that will // be carried out through the rest of the encoding. Multiple // frames are used in a hierarchical system, however most JPEG's // only contain a single frame. // Progressive or baseline? progressive = marker == JPEGMarker.SOF2; jpegFrames.Add(new JPEGFrame()); frame = (JPEGFrame)jpegFrames[jpegFrames.Count - 1]; frame.ProgressUpdateMethod = new Action <long>(UpdateStreamProgress); // Skip the frame length. jpegReader.ReadShort(); // Bits percision, either 8 or 12. frame.setPrecision(jpegReader.ReadByte()); // Scan lines (height) frame.ScanLines = jpegReader.ReadShort(); // Scan samples per line (width) frame.SamplesPerLine = jpegReader.ReadShort(); // Number of Color Components (channels). frame.ComponentCount = jpegReader.ReadByte(); DecodeProgress.Height = frame.Height; DecodeProgress.Width = frame.Width; DecodeProgress.SizeReady = true; if (DecodeProgressChanged != null) { DecodeProgressChanged(this, DecodeProgress); } // Add all of the necessary components to the frame. for (int i = 0; i < frame.ComponentCount; i++) { byte compId = jpegReader.ReadByte(); byte sampleFactors = jpegReader.ReadByte(); byte qTableId = jpegReader.ReadByte(); byte sampleHFactor = (byte)(sampleFactors >> 4); byte sampleVFactor = (byte)(sampleFactors & 0x0f); frame.AddComponent(compId, sampleHFactor, sampleVFactor, qTableId); } break; case JPEGMarker.DHT: // DHT non-SOF Marker - Huffman Table is required for decoding // the JPEG stream, when we receive a marker we load in first // the table length (16 bits), the table class (4 bits), table // identifier (4 bits), then we load in 16 bytes and each byte // represents the count of bytes to load in for each of the 16 // bytes. We load this into an array to use later and move on 4 // huffman tables can only be used in an image. int huffmanLength = (jpegReader.ReadShort() - 2); // Keep looping until we are out of length. int index = huffmanLength; // Multiple tables may be defined within a DHT marker. This // will keep reading until there are no tables left, most // of the time there are just one tables. while (index > 0) { // Read the identifier information and class // information about the Huffman table, then read the // 16 byte codelength in and read in the Huffman values // and put it into table info. byte huffmanInfo = jpegReader.ReadByte(); byte tableClass = (byte)(huffmanInfo >> 4); byte huffmanIndex = (byte)(huffmanInfo & 0x0f); short[] codeLength = new short[16]; for (int i = 0; i < codeLength.Length; i++) { codeLength[i] = jpegReader.ReadByte(); } int huffmanValueLen = 0; for (int i = 0; i < 16; i++) { huffmanValueLen += codeLength[i]; } index -= (huffmanValueLen + 17); short[] huffmanVal = new short[huffmanValueLen]; for (int i = 0; i < huffmanVal.Length; i++) { huffmanVal[i] = jpegReader.ReadByte(); } // Assign DC Huffman Table. if (tableClass == HuffmanTable.JPEG_DC_TABLE) { dcTables[(int)huffmanIndex] = new JpegHuffmanTable(codeLength, huffmanVal); } // Assign AC Huffman Table. else if (tableClass == HuffmanTable.JPEG_AC_TABLE) { acTables[(int)huffmanIndex] = new JpegHuffmanTable(codeLength, huffmanVal); } } break; case JPEGMarker.DQT: // DQT non-SOF Marker - This defines the quantization // coeffecients, this allows us to figure out the quality of // compression and unencode the data. The data is loaded and // then stored in to an array. short quantizationLength = (short)(jpegReader.ReadShort() - 2); for (int j = 0; j < quantizationLength / 65; j++) { byte quantSpecs = jpegReader.ReadByte(); int[] quantData = new int[64]; if ((byte)(quantSpecs >> 4) == 0) // Precision 8 bit. { for (int i = 0; i < 64; i++) { quantData[i] = jpegReader.ReadByte(); } } else if ((byte)(quantSpecs >> 4) == 1) // Precision 16 bit. { for (int i = 0; i < 64; i++) { quantData[i] = jpegReader.ReadShort(); } } qTables[(int)(quantSpecs & 0x0f)] = new JpegQuantizationTable(quantData); } break; case JPEGMarker.SOS: Debug.WriteLine("Start of Scan (SOS)"); // SOS non-SOF Marker - Start Of Scan Marker, this is where the // actual data is stored in a interlaced or non-interlaced with // from 1-4 components of color data, if three components most // likely a YCrCb model, this is a fairly complex process. // Read in the scan length. //ushort scanLen = jpegReader.ReadShort(); // Number of components in the scan. byte numberOfComponents = jpegReader.ReadByte(); byte[] componentSelector = new byte[numberOfComponents]; for (int i = 0; i < numberOfComponents; i++) { // Component ID, packed byte containing the Id for the // AC table and DC table. byte componentID = jpegReader.ReadByte(); byte tableInfo = jpegReader.ReadByte(); int DC = (tableInfo >> 4) & 0x0f; int AC = (tableInfo) & 0x0f; frame.setHuffmanTables(componentID, acTables[(byte)AC], dcTables[(byte)DC]); componentSelector[i] = componentID; } byte startSpectralSelection = jpegReader.ReadByte(); byte endSpectralSelection = jpegReader.ReadByte(); byte successiveApproximation = jpegReader.ReadByte(); #region Baseline JPEG Scan Decoding if (!progressive) { frame.DecodeScanBaseline(numberOfComponents, componentSelector, resetInterval, jpegReader, ref marker); haveMarker = true; // use resultant marker for the next switch(..) } #endregion #region Progressive JPEG Scan Decoding if (progressive) { frame.DecodeScanProgressive( successiveApproximation, startSpectralSelection, endSpectralSelection, numberOfComponents, componentSelector, resetInterval, jpegReader, ref marker); haveMarker = true; // use resultant marker for the next switch(..) } #endregion break; case JPEGMarker.DRI: jpegReader.BaseStream.Seek(2, System.IO.SeekOrigin.Current); resetInterval = jpegReader.ReadShort(); break; // Defines the number of lines. (Not usually present) case JPEGMarker.DNL: frame.ScanLines = jpegReader.ReadShort(); break; // End of Image. Finish the decode. case JPEGMarker.EOI: if (jpegFrames.Count == 0) { throw new NotSupportedException("No JPEG frames could be located."); } else if (jpegFrames.Count == 1) { // Only one frame, JPEG Non-Heirarchial Frame. byte[][,] raster = Image.CreateRaster(frame.Width, frame.Height, frame.ComponentCount); IList <JpegComponent> components = frame.Scan.Components; int totalSteps = components.Count * 3; // Three steps per loop int stepsFinished = 0; for (int i = 0; i < components.Count; i++) { JpegComponent comp = components[i]; comp.QuantizationTable = qTables[comp.quant_id].Table; // 1. Quantize comp.quantizeData(); UpdateProgress(++stepsFinished, totalSteps); // 2. Run iDCT (expensive) comp.idctData(); UpdateProgress(++stepsFinished, totalSteps); // 3. Scale the image and write the data to the raster. comp.writeDataScaled(raster, i, BlockUpsamplingMode); UpdateProgress(++stepsFinished, totalSteps); // Ensure garbage collection. comp = null; GC.Collect(); } // Grayscale Color Image (1 Component). if (frame.ComponentCount == 1) { ColorModel cm = new ColorModel() { ColorSpace = ColorSpace.Gray, Opaque = true }; image = new Image(cm, raster); } // YCbCr Color Image (3 Components). else if (frame.ComponentCount == 3) { ColorModel cm = new ColorModel() { ColorSpace = ColorSpace.YCbCr, Opaque = true }; image = new Image(cm, raster); } // Possibly CMYK or RGBA ? else { throw new NotSupportedException("Unsupported Color Mode: 4 Component Color Mode found."); } // If needed, convert centimeters to inches. Func <double, double> conv = x => Units == UnitType.Inches ? x : x / 2.54; image.DensityX = conv(XDensity); image.DensityY = conv(YDensity); //height = frame.Height; //width = frame.Width; } else { // JPEG Heirarchial Frame throw new NotSupportedException("Unsupported Codec Type: Hierarchial JPEG"); } break; // Only SOF0 (baseline) and SOF2 (progressive) are supported by FJCore case JPEGMarker.SOF1: case JPEGMarker.SOF3: case JPEGMarker.SOF5: case JPEGMarker.SOF6: case JPEGMarker.SOF7: case JPEGMarker.SOF9: case JPEGMarker.SOF10: case JPEGMarker.SOF11: case JPEGMarker.SOF13: case JPEGMarker.SOF14: case JPEGMarker.SOF15: throw new NotSupportedException("Unsupported codec type."); default: break; // ignore } #endregion switch over markers if (haveMarker) { haveMarker = false; } else { try { marker = jpegReader.GetNextMarker(); } catch (System.IO.EndOfStreamException) { break; /* done reading the file */ } } } DecodedJpeg result = new DecodedJpeg(image, headers); return(result); }