Initial commit
This commit is contained in:
commit
262601edb8
2025 files changed
+218754
No files matched your search
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Codec.cs:
|
||||
//
|
||||
// Author:
|
||||
// Ruben Vermeersch (ruben@savanne.be)
|
||||
// Mike Gemuende (mike@gemuende.de)
|
||||
//
|
||||
// Copyright (C) 2009 Ruben Vermeersch
|
||||
// Copyright (C) 2009 Mike Gemuende
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License version
|
||||
// 2.1 as published by the Free Software Foundation.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
//
|
||||
|
||||
namespace TagLib.Jpeg
|
||||
{
|
||||
/// <summary>
|
||||
/// A Jpeg photo codec. Contains basic photo details.
|
||||
/// </summary>
|
||||
public class Codec : Image.Codec
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Gets a text description of the media represented by the
|
||||
/// current instance.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string" /> object containing a description
|
||||
/// of the media represented by the current instance.
|
||||
/// </value>
|
||||
public override string Description { get { return "JFIF File"; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Codec" /> with the given width
|
||||
/// and height.
|
||||
/// </summary>
|
||||
/// <param name="width">
|
||||
/// The width of the photo.
|
||||
/// </param>
|
||||
/// <param name="height">
|
||||
/// The height of the photo.
|
||||
/// </param>
|
||||
/// <param name="quality">
|
||||
/// The quality of the photo.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A new <see cref="Codec" /> instance.
|
||||
/// </returns>
|
||||
public Codec (int width, int height, int quality)
|
||||
: base (width, height, quality) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3908c6bb8ab4c15d89ac169ef63ba12d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,813 @@
|
||||
//
|
||||
// File.cs: Provides tagging for Jpeg files
|
||||
//
|
||||
// Author:
|
||||
// Ruben Vermeersch (ruben@savanne.be)
|
||||
// Mike Gemuende (mike@gemuende.de)
|
||||
// Stephane Delcroix (stephane@delcroix.org)
|
||||
//
|
||||
// Copyright (C) 2009 Ruben Vermeersch
|
||||
// Copyright (C) 2009 Mike Gemuende
|
||||
// Copyright (c) 2009 Stephane Delcroix
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License version
|
||||
// 2.1 as published by the Free Software Foundation.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using TagLib.IFD;
|
||||
using TagLib.Image;
|
||||
using TagLib.Xmp;
|
||||
|
||||
namespace TagLib.Jpeg
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This class extends <see cref="TagLib.Image.ImageBlockFile" /> to provide tagging
|
||||
/// and properties support for Jpeg files.
|
||||
/// </summary>
|
||||
[SupportedMimeType ("taglib/jpg", "jpg")]
|
||||
[SupportedMimeType ("taglib/jpeg", "jpeg")]
|
||||
[SupportedMimeType ("taglib/jpe", "jpe")]
|
||||
[SupportedMimeType ("taglib/jif", "jif")]
|
||||
[SupportedMimeType ("taglib/jfif", "jfif")]
|
||||
[SupportedMimeType ("taglib/jfi", "jfi")]
|
||||
[SupportedMimeType ("image/jpeg")]
|
||||
public class File : ImageBlockFile
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The magic bits used to recognize an Exif segment
|
||||
/// </summary>
|
||||
static readonly string EXIF_IDENTIFIER = "Exif\0\0";
|
||||
|
||||
/// <summary>
|
||||
/// The magic strings used to identifiy an IPTC-IIM section
|
||||
/// </summary>
|
||||
static readonly string IPTC_IIM_IDENTIFIER = "Photoshop 3.0\u00008BIM\u0004\u0004";
|
||||
|
||||
/// <summary>
|
||||
/// Standard (empty) JFIF header to add, if no one is contained
|
||||
/// </summary>
|
||||
static readonly byte[] BASIC_JFIF_HEADER = new byte[] {
|
||||
// segment maker
|
||||
0xFF, (byte) Marker.APP0,
|
||||
|
||||
// segment size
|
||||
0x00, 0x10,
|
||||
|
||||
// segment data
|
||||
0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01,
|
||||
0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00
|
||||
};
|
||||
|
||||
|
||||
#region Private Fields
|
||||
|
||||
/// <summary>
|
||||
/// Contains the media properties.
|
||||
/// </summary>
|
||||
Properties properties;
|
||||
|
||||
/// <summary>
|
||||
/// For now, we do not allow to change the jfif header. As long as this is
|
||||
/// the case, the header is kept as it is.
|
||||
/// </summary>
|
||||
ByteVector jfif_header;
|
||||
|
||||
/// <summary>
|
||||
/// The image width, as parsed from the Frame
|
||||
/// </summary>
|
||||
ushort width;
|
||||
|
||||
/// <summary>
|
||||
/// The image height, as parsed from the Frame
|
||||
/// </summary>
|
||||
ushort height;
|
||||
|
||||
/// <summary>
|
||||
/// Quality of the image, stored as we parse the file
|
||||
/// </summary>
|
||||
int quality;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and initializes a new instance of <see
|
||||
/// cref="File" /> for a specified path in the local file
|
||||
/// system and specified read style.
|
||||
/// </summary>
|
||||
/// <param name="path">
|
||||
/// A <see cref="string" /> object containing the path of the
|
||||
/// file to use in the new instance.
|
||||
/// </param>
|
||||
/// <param name="propertiesStyle">
|
||||
/// A <see cref="ReadStyle" /> value specifying at what level
|
||||
/// of accuracy to read the media properties, or <see
|
||||
/// cref="ReadStyle.None" /> to ignore the properties.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="path" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
public File (string path, ReadStyle propertiesStyle)
|
||||
: this (new LocalFileAbstraction (path), propertiesStyle)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and initializes a new instance of <see
|
||||
/// cref="File" /> for a specified path in the local file
|
||||
/// system.
|
||||
/// </summary>
|
||||
/// <param name="path">
|
||||
/// A <see cref="string" /> object containing the path of the
|
||||
/// file to use in the new instance.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="path" /> is <see langword="null" />.
|
||||
/// </exception>
|
||||
public File (string path)
|
||||
: this (path, ReadStyle.Average)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and initializes a new instance of <see
|
||||
/// cref="File" /> for a specified file abstraction and
|
||||
/// specified read style.
|
||||
/// </summary>
|
||||
/// <param name="abstraction">
|
||||
/// A <see cref="TagLib.File.IFileAbstraction" /> object to use when
|
||||
/// reading from and writing to the file.
|
||||
/// </param>
|
||||
/// <param name="propertiesStyle">
|
||||
/// A <see cref="ReadStyle" /> value specifying at what level
|
||||
/// of accuracy to read the media properties, or <see
|
||||
/// cref="ReadStyle.None" /> to ignore the properties.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="abstraction" /> is <see langword="null"
|
||||
/// />.
|
||||
/// </exception>
|
||||
public File (IFileAbstraction abstraction, ReadStyle propertiesStyle)
|
||||
: base (abstraction)
|
||||
{
|
||||
Read (propertiesStyle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and initializes a new instance of <see
|
||||
/// cref="File" /> for a specified file abstraction.
|
||||
/// </summary>
|
||||
/// <param name="abstraction">
|
||||
/// A <see cref="TagLib.File.IFileAbstraction" /> object to use when
|
||||
/// reading from and writing to the file.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="abstraction" /> is <see langword="null"
|
||||
/// />.
|
||||
/// </exception>
|
||||
protected File (IFileAbstraction abstraction)
|
||||
: this (abstraction, ReadStyle.Average)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the media properties of the file represented by the
|
||||
/// current instance.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="TagLib.Properties" /> object containing the
|
||||
/// media properties of the file represented by the current
|
||||
/// instance.
|
||||
/// </value>
|
||||
public override Properties Properties {
|
||||
get { return properties; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets a tag of a specified type from the current instance, optionally creating a
|
||||
/// new tag if possible.
|
||||
/// </summary>
|
||||
public override Tag GetTag (TagTypes type, bool create)
|
||||
{
|
||||
if (type == TagTypes.XMP) {
|
||||
foreach (Tag tag in ImageTag.AllTags) {
|
||||
if ((tag.TagTypes & type) == type || (tag.TagTypes & TagTypes.IPTCIIM) != 0)
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
if (type == TagTypes.IPTCIIM && create) {
|
||||
// FIXME: don't know how to create IPTCIIM tags
|
||||
return base.GetTag (type, false);
|
||||
}
|
||||
|
||||
return base.GetTag (type, create);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the changes made in the current instance to the
|
||||
/// file it represents.
|
||||
/// </summary>
|
||||
public override void Save ()
|
||||
{
|
||||
// Boilerplate
|
||||
PreSave ();
|
||||
|
||||
Mode = AccessMode.Write;
|
||||
try {
|
||||
WriteMetadata ();
|
||||
|
||||
TagTypesOnDisk = TagTypes;
|
||||
} finally {
|
||||
Mode = AccessMode.Closed;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Reads the information from file with a specified read style.
|
||||
/// </summary>
|
||||
/// <param name="propertiesStyle">
|
||||
/// A <see cref="ReadStyle" /> value specifying at what level
|
||||
/// of accuracy to read the media properties, or <see
|
||||
/// cref="ReadStyle.None" /> to ignore the properties.
|
||||
/// </param>
|
||||
void Read (ReadStyle propertiesStyle)
|
||||
{
|
||||
Mode = AccessMode.Read;
|
||||
try {
|
||||
ImageTag = new CombinedImageTag (TagTypes.XMP | TagTypes.TiffIFD | TagTypes.JpegComment | TagTypes.IPTCIIM);
|
||||
|
||||
ValidateHeader ();
|
||||
ReadMetadata ();
|
||||
|
||||
TagTypesOnDisk = TagTypes;
|
||||
|
||||
if ((propertiesStyle & ReadStyle.Average) != 0)
|
||||
properties = ExtractProperties ();
|
||||
|
||||
} finally {
|
||||
Mode = AccessMode.Closed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to extract the media properties of the main
|
||||
/// photo.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="Properties" /> object with a best effort guess
|
||||
/// at the right values. When no guess at all can be made,
|
||||
/// <see langword="null" /> is returned.
|
||||
/// </returns>
|
||||
Properties ExtractProperties ()
|
||||
{
|
||||
if (width > 0 && height > 0)
|
||||
return new Properties (TimeSpan.Zero, new Codec (width, height, quality));
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates if the opened file is actually a JPEG.
|
||||
/// </summary>
|
||||
void ValidateHeader ()
|
||||
{
|
||||
ByteVector segment = ReadBlock (2);
|
||||
if (segment.ToUShort () != 0xFFD8)
|
||||
throw new CorruptFileException ("Expected SOI marker at the start of the file.");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads a segment marker for a segment starting at current position.
|
||||
/// The second byte of the marker is returned, since the first is equal
|
||||
/// to 0xFF in every case.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="TagLib.Jpeg.Marker"/> with the second byte of the segment marker.
|
||||
/// </returns>
|
||||
Marker ReadSegmentMarker ()
|
||||
{
|
||||
ByteVector segment_header = ReadBlock (2);
|
||||
|
||||
if (segment_header.Count != 2)
|
||||
throw new CorruptFileException ("Could not read enough bytes for segment maker");
|
||||
|
||||
if (segment_header[0] != 0xFF)
|
||||
throw new CorruptFileException ("Start of Segment expected at " + (Tell - 2));
|
||||
|
||||
return (Marker)segment_header[1];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads the size of a segment at the current position.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.UInt16"/> with the size of the current segment.
|
||||
/// </returns>
|
||||
ushort ReadSegmentSize ()
|
||||
{
|
||||
long position = Tell;
|
||||
|
||||
ByteVector segment_size_bytes = ReadBlock (2);
|
||||
|
||||
if (segment_size_bytes.Count != 2)
|
||||
throw new CorruptFileException ("Could not read enough bytes to determine segment size");
|
||||
|
||||
ushort segment_size = segment_size_bytes.ToUShort ();
|
||||
|
||||
// the size itself must be contained in the segment size
|
||||
// so the smallest (theoretically) possible number of bytes if 2
|
||||
if (segment_size < 2)
|
||||
throw new CorruptFileException ($"Invalid segment size ({segment_size} bytes)");
|
||||
|
||||
long length = 0;
|
||||
try {
|
||||
length = Length;
|
||||
} catch (Exception) {
|
||||
// Probably not supported by stream.
|
||||
}
|
||||
|
||||
if (length > 0 && position + segment_size >= length)
|
||||
throw new CorruptFileException ("Segment size exceeds file size");
|
||||
|
||||
return segment_size;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the metadata from the current file by reading every segment in file.
|
||||
/// Method should be called with read position at first segment marker.
|
||||
/// </summary>
|
||||
void ReadMetadata ()
|
||||
{
|
||||
// loop while marker is not EOI and not the data segment
|
||||
while (true) {
|
||||
Marker marker = ReadSegmentMarker ();
|
||||
|
||||
// we stop parsing when the end of file (EOI) or the begin of the
|
||||
// data segment is reached (SOS)
|
||||
// the second case is a trade-off between tolerant and fast parsing
|
||||
if (marker == Marker.EOI || marker == Marker.SOS)
|
||||
break;
|
||||
|
||||
long position = Tell;
|
||||
ushort segment_size = ReadSegmentSize ();
|
||||
|
||||
// segment size contains 2 bytes of the size itself, so the
|
||||
// pure data size is this (and the cast is save)
|
||||
ushort data_size = (ushort)(segment_size - 2);
|
||||
|
||||
switch (marker) {
|
||||
case Marker.APP0: // possibly JFIF header
|
||||
ReadJFIFHeader (data_size);
|
||||
break;
|
||||
|
||||
case Marker.APP1: // possibly Exif or Xmp data found
|
||||
ReadAPP1Segment (data_size);
|
||||
break;
|
||||
|
||||
case Marker.APP13: // possibly IPTC-IIM
|
||||
ReadAPP13Segment (data_size);
|
||||
break;
|
||||
|
||||
case Marker.COM: // Comment segment found
|
||||
ReadCOMSegment (data_size);
|
||||
break;
|
||||
|
||||
case Marker.SOF0:
|
||||
case Marker.SOF1:
|
||||
case Marker.SOF2:
|
||||
case Marker.SOF3:
|
||||
case Marker.SOF9:
|
||||
case Marker.SOF10:
|
||||
case Marker.SOF11:
|
||||
ReadSOFSegment (data_size, marker);
|
||||
break;
|
||||
|
||||
case Marker.DQT: // Quantization table(s), use it to guess quality
|
||||
ReadDQTSegment (data_size);
|
||||
break;
|
||||
}
|
||||
|
||||
// set position to next segment and start with next segment marker
|
||||
Seek (position + segment_size, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a JFIF header at current position
|
||||
/// </summary>
|
||||
void ReadJFIFHeader (ushort length)
|
||||
{
|
||||
// JFIF header should be contained as first segment
|
||||
// SOI marker + APP0 Marker + segment size = 6 bytes
|
||||
if (Tell != 6)
|
||||
return;
|
||||
|
||||
if (ReadBlock (5).ToString ().Equals ("JFIF\0")) {
|
||||
|
||||
// store the JFIF header as it is
|
||||
Seek (2, SeekOrigin.Begin);
|
||||
jfif_header = ReadBlock (length + 2 + 2);
|
||||
|
||||
AddMetadataBlock (2, length + 2 + 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an APP1 segment to find EXIF or XMP metadata.
|
||||
/// </summary>
|
||||
/// <param name="length">
|
||||
/// The length of the segment that will be read.
|
||||
/// </param>
|
||||
void ReadAPP1Segment (ushort length)
|
||||
{
|
||||
long position = Tell;
|
||||
ByteVector data = null;
|
||||
|
||||
// for an Exif segment, the data block consists of 14 bytes of:
|
||||
// * 6 bytes Exif identifier string
|
||||
// * 2 bytes bigendian indication MM (or II)
|
||||
// * 2 bytes Tiff magic number (42)
|
||||
// * 4 bytes offset of the first IFD in this segment
|
||||
//
|
||||
// the last two points are alreay encoded according to
|
||||
// big- or littleendian
|
||||
int exif_header_length = 14;
|
||||
|
||||
// could be an Exif segment
|
||||
if ((ImageTag.TagTypes & TagTypes.TiffIFD) == 0x00 && length >= exif_header_length) {
|
||||
|
||||
data = ReadBlock (exif_header_length);
|
||||
|
||||
if (data.Count == exif_header_length
|
||||
&& data.Mid (0, 6).ToString ().Equals (EXIF_IDENTIFIER)) {
|
||||
|
||||
bool is_bigendian = data.Mid (6, 2).ToString ().Equals ("MM");
|
||||
|
||||
ushort magic = data.Mid (8, 2).ToUShort (is_bigendian);
|
||||
if (magic != 42)
|
||||
throw new Exception ($"Invalid TIFF magic: {magic}");
|
||||
|
||||
uint ifd_offset = data.Mid (10, 4).ToUInt (is_bigendian);
|
||||
|
||||
var exif = new IFDTag ();
|
||||
var reader = new IFDReader (this, is_bigendian, exif.Structure, position + 6, ifd_offset, (uint)(length - 6));
|
||||
reader.Read ();
|
||||
ImageTag.AddTag (exif);
|
||||
|
||||
AddMetadataBlock (position - 4, length + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int xmp_header_length = XmpTag.XAP_NS.Length + 1;
|
||||
|
||||
// could be an Xmp segment
|
||||
if ((ImageTag.TagTypes & TagTypes.XMP) == 0x00 && length >= xmp_header_length) {
|
||||
|
||||
// if already data is read for determining the Exif segment,
|
||||
// just read the remaining bytes.
|
||||
// NOTE: that (exif_header_length < xmp_header_length) holds
|
||||
if (data == null)
|
||||
data = ReadBlock (xmp_header_length);
|
||||
else
|
||||
data.Add (ReadBlock (xmp_header_length - exif_header_length));
|
||||
|
||||
if (data.ToString ().Equals (XmpTag.XAP_NS + "\0")) {
|
||||
ByteVector xmp_data = ReadBlock (length - xmp_header_length);
|
||||
|
||||
ImageTag.AddTag (new XmpTag (xmp_data.ToString (), this));
|
||||
|
||||
AddMetadataBlock (position - 4, length + 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an APP13 segment to find IPTC-IIM metadata.
|
||||
/// </summary>
|
||||
/// <param name="length">
|
||||
/// The length of the segment that will be read.
|
||||
/// </param>
|
||||
/// <remarks>More info and specs for IPTC-IIM:
|
||||
/// - Guidelines for Handling Image Metadata (http://www.metadataworkinggroup.org/specs/)
|
||||
/// - IPTC Standard Photo Metadata (July 2010) (http://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata-201007_1.pdf)
|
||||
/// - Extracting IPTC header information from JPEG images (http://www.codeproject.com/KB/graphics/iptc.aspx?fid=2301&df=90&mpp=25&noise=3&prof=False&sort=Position&view=Quick&fr=51#xx0xx)
|
||||
/// - Reading IPTC APP14 Segment Header Information from JPEG Images (http://www.codeproject.com/KB/graphics/ReadingIPTCAPP14.aspx?q=iptc)
|
||||
/// </remarks>
|
||||
void ReadAPP13Segment (ushort length)
|
||||
{
|
||||
// TODO: if both IPTC-IIM and XMP metadata is contained in a file, we should read
|
||||
// a IPTC-IIM checksum and compare that with the checksum built over the IIM block.
|
||||
// Depending on the result we should prefer the information from XMP or IIM.
|
||||
// Right now we always prefer XMP.
|
||||
|
||||
var data = ReadBlock (length);
|
||||
|
||||
// The APP13 segment consists of:
|
||||
// - the string "Photoshop 3.0\u0000"
|
||||
// - followed by "8BIM"
|
||||
// - and then the section type "\u0004\u0004".
|
||||
// There might be multiple 8BIM sections with different types, but we're following
|
||||
// YAGNI for now and only deal with the one we're interested in (and hope that it's
|
||||
// the first one).
|
||||
var iptc_iim_length = IPTC_IIM_IDENTIFIER.Length;
|
||||
if (length < iptc_iim_length || data.Mid (0, iptc_iim_length) != IPTC_IIM_IDENTIFIER)
|
||||
return;
|
||||
|
||||
// PS6 introduced a new header with variable length text
|
||||
var headerInfoLen = data.Mid (iptc_iim_length, 1).ToUShort ();
|
||||
int lenToSkip;
|
||||
if (headerInfoLen > 0) {
|
||||
// PS6 header: 1 byte headerinfolen + headerinfo + 2 bytes 00 padding (?) + 2 bytes length
|
||||
lenToSkip = 1 + headerInfoLen + 4;
|
||||
} else {
|
||||
//old style: 4 bytes 00 padding (?) + 2 bytes length
|
||||
lenToSkip = 6;
|
||||
}
|
||||
data.RemoveRange (0, iptc_iim_length + lenToSkip);
|
||||
|
||||
var reader = new IIM.IIMReader (data);
|
||||
var tag = reader.Process ();
|
||||
if (tag != null)
|
||||
ImageTag.AddTag (tag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the metadata back to file. All metadata is stored in the first segments
|
||||
/// of the file.
|
||||
/// </summary>
|
||||
void WriteMetadata ()
|
||||
{
|
||||
// first render all metadata segments to a ByteVector before the
|
||||
// file is touched ...
|
||||
var data = new ByteVector ();
|
||||
|
||||
// existing jfif header is retained, otherwise a standard one
|
||||
// is created
|
||||
if (jfif_header != null)
|
||||
data.Add (jfif_header);
|
||||
else
|
||||
data.Add (BASIC_JFIF_HEADER);
|
||||
|
||||
data.Add (RenderExifSegment ());
|
||||
data.Add (RenderXMPSegment ());
|
||||
data.Add (RenderCOMSegment ());
|
||||
|
||||
SaveMetadata (data, 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="ByteVector"/> for the Exif segment of this file
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="ByteVector"/> with the whole Exif segment, if exif tags
|
||||
/// exists, otherwise null.
|
||||
/// </returns>
|
||||
ByteVector RenderExifSegment ()
|
||||
{
|
||||
// Check, if IFD0 is contained
|
||||
IFDTag exif = ImageTag.Exif;
|
||||
if (exif == null)
|
||||
return null;
|
||||
|
||||
// first IFD starts at 8
|
||||
uint first_ifd_offset = 8;
|
||||
|
||||
// Render IFD0
|
||||
// FIXME: store endianess and use it here
|
||||
var renderer = new IFDRenderer (true, exif.Structure, first_ifd_offset);
|
||||
ByteVector exif_data = renderer.Render ();
|
||||
|
||||
uint segment_size = (uint)(first_ifd_offset + exif_data.Count + 2 + 6);
|
||||
|
||||
// do not render data segments, which cannot fit into the possible segment size
|
||||
if (segment_size > ushort.MaxValue)
|
||||
throw new Exception ("Exif Segment is too big to render");
|
||||
|
||||
// Create whole segment
|
||||
var data = new ByteVector (new byte[] { 0xFF, (byte)Marker.APP1 }) {
|
||||
ByteVector.FromUShort ((ushort)segment_size),
|
||||
"Exif\0\0",
|
||||
ByteVector.FromString ("MM", StringType.Latin1),
|
||||
ByteVector.FromUShort (42),
|
||||
ByteVector.FromUInt (first_ifd_offset),
|
||||
|
||||
// Add ifd data itself
|
||||
exif_data
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="ByteVector"/> for the Xmp segment of this file
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="ByteVector"/> with the whole Xmp segment, if xmp tags
|
||||
/// exists, otherwise null.
|
||||
/// </returns>
|
||||
ByteVector RenderXMPSegment ()
|
||||
{
|
||||
// Check, if XmpTag is contained
|
||||
XmpTag xmp = ImageTag.Xmp;
|
||||
if (xmp == null)
|
||||
return null;
|
||||
|
||||
ByteVector xmp_data = XmpTag.XAP_NS + "\0";
|
||||
xmp_data.Add (xmp.Render ());
|
||||
|
||||
uint segment_size = (uint)(2 + xmp_data.Count);
|
||||
|
||||
// do not render data segments, which cannot fit into the possible segment size
|
||||
if (segment_size > ushort.MaxValue)
|
||||
throw new Exception ("XMP Segment is too big to render");
|
||||
|
||||
// Create whole segment
|
||||
var data = new ByteVector (new byte[] { 0xFF, (byte)Marker.APP1 }) {
|
||||
ByteVector.FromUShort ((ushort)segment_size),
|
||||
xmp_data
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads a COM segment to find the JPEG comment.
|
||||
/// </summary>
|
||||
/// <param name="length">
|
||||
/// The length of the segment that will be read.
|
||||
/// </param>
|
||||
void ReadCOMSegment (int length)
|
||||
{
|
||||
if ((ImageTag.TagTypes & TagTypes.JpegComment) != 0x00)
|
||||
return;
|
||||
|
||||
long position = Tell;
|
||||
|
||||
JpegCommentTag com_tag;
|
||||
|
||||
if (length == 0) {
|
||||
com_tag = new JpegCommentTag ();
|
||||
} else {
|
||||
ByteVector data = ReadBlock (length);
|
||||
|
||||
int terminator = data.Find ("\0", 0);
|
||||
|
||||
if (terminator < 0)
|
||||
com_tag = new JpegCommentTag (data.ToString ());
|
||||
else
|
||||
com_tag = new JpegCommentTag (data.Mid (0, terminator).ToString ());
|
||||
}
|
||||
|
||||
ImageTag.AddTag (com_tag);
|
||||
AddMetadataBlock (position - 4, length + 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="ByteVector"/> for the comment segment of this file
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="ByteVector"/> with the whole comment segment, if a comment tag
|
||||
/// exists, otherwise null.
|
||||
/// </returns>
|
||||
ByteVector RenderCOMSegment ()
|
||||
{
|
||||
// check, if Comment is contained
|
||||
if (!(GetTag (TagTypes.JpegComment) is JpegCommentTag com_tag))
|
||||
return null;
|
||||
|
||||
// create comment data
|
||||
var com_data = ByteVector.FromString (com_tag.Value + "\0", StringType.Latin1);
|
||||
|
||||
uint segment_size = (uint)(2 + com_data.Count);
|
||||
|
||||
// do not render data segments, which cannot fit into the possible segment size
|
||||
if (segment_size > ushort.MaxValue)
|
||||
throw new Exception ("Comment Segment is too big to render");
|
||||
|
||||
// create segment
|
||||
var data = new ByteVector (new byte[] { 0xFF, (byte)Marker.COM }) {
|
||||
ByteVector.FromUShort ((ushort)segment_size),
|
||||
|
||||
com_data
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and parse a SOF segment
|
||||
/// </summary>
|
||||
/// <param name="length">
|
||||
/// The length of the segment that will be read.
|
||||
/// </param>
|
||||
/// <param name="marker">
|
||||
/// The SOFx marker.
|
||||
/// </param>
|
||||
void ReadSOFSegment (int length, Marker marker)
|
||||
{
|
||||
#pragma warning disable 219 // Assigned, never read
|
||||
byte p = ReadBlock (1)[0]; //precision
|
||||
#pragma warning restore 219
|
||||
|
||||
//FIXME: according to specs, height could be 0 here, and should be retrieved from the DNL marker
|
||||
height = ReadBlock (2).ToUShort ();
|
||||
width = ReadBlock (2).ToUShort ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the DQT Segment, and Guesstimate the image quality from it
|
||||
/// </summary>
|
||||
/// <param name="length">
|
||||
/// The length of the segment that will be read
|
||||
/// </param>
|
||||
void ReadDQTSegment (int length)
|
||||
{
|
||||
// See CCITT Rec. T.81 (1992 E), B.2.4.1 (p39) for DQT syntax
|
||||
while (length > 0) {
|
||||
|
||||
byte pqtq = ReadBlock (1)[0]; length--;
|
||||
byte pq = (byte)(pqtq >> 4); //0 indicates 8-bit Qk, 1 indicates 16-bit Qk
|
||||
byte tq = (byte)(pqtq & 0x0f); //table index;
|
||||
int[] table = null;
|
||||
switch (tq) {
|
||||
case 0:
|
||||
table = Table.StandardLuminanceQuantization;
|
||||
break;
|
||||
case 1:
|
||||
table = Table.StandardChrominanceQuantization;
|
||||
break;
|
||||
}
|
||||
|
||||
bool allones = true; //check for all-ones tables (q=100)
|
||||
double cumsf = 0.0;
|
||||
//double cumsf2 = 0.0;
|
||||
for (int row = 0; row < 8; row++) {
|
||||
for (int col = 0; col < 8; col++) {
|
||||
ushort val = ReadBlock (pq == 1 ? 2 : 1).ToUShort (); length -= (pq + 1);
|
||||
if (table != null) {
|
||||
double x = 100.0 * val / table[row * 8 + col]; //Scaling factor in percent
|
||||
cumsf += x;
|
||||
//cumsf2 += x*x;
|
||||
allones = allones && (val == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (table != null) {
|
||||
double local_q;
|
||||
cumsf /= 64.0; // mean scale factor
|
||||
//cumfs2 /= 64.0;
|
||||
//double variance = cumsf2 - (cumsf * cumsf);
|
||||
|
||||
if (allones)
|
||||
local_q = 100.0;
|
||||
else if (cumsf <= 100.0)
|
||||
local_q = (200.0 - cumsf) / 2.0;
|
||||
else
|
||||
local_q = 5000.0 / cumsf;
|
||||
quality = Math.Max (quality, (int)local_q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a9ddad01b29bda38bead0dddfffb8ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// JpegCommentTag.cs:
|
||||
//
|
||||
// Author:
|
||||
// Ruben Vermeersch (ruben@savanne.be)
|
||||
//
|
||||
// Copyright (C) 2009 Ruben Vermeersch
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License version
|
||||
// 2.1 as published by the Free Software Foundation.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
//
|
||||
|
||||
using TagLib.Image;
|
||||
|
||||
namespace TagLib.Jpeg
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the JPEG comment.
|
||||
/// </summary>
|
||||
public class JpegCommentTag : ImageTag
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="value">
|
||||
/// The value of the comment.
|
||||
/// </param>
|
||||
public JpegCommentTag (string value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor. Creates a new empty comment.
|
||||
/// </summary>
|
||||
public JpegCommentTag ()
|
||||
{
|
||||
Value = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The value of the comment represented by the current instance.
|
||||
/// </summary>
|
||||
public string Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the comment for the image described
|
||||
/// by the current instance.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="string" /> containing the comment of the
|
||||
/// current instace.
|
||||
/// </value>
|
||||
public override string Comment {
|
||||
get { return Value; }
|
||||
set { Value = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tag types contained in the current instance.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Always <see cref="TagTypes.JpegComment" />.
|
||||
/// </value>
|
||||
public override TagTypes TagTypes {
|
||||
get { return TagTypes.JpegComment; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the values stored in the current instance.
|
||||
/// </summary>
|
||||
public override void Clear ()
|
||||
{
|
||||
Value = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae56889b076202f52893aefe5174b1a6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,350 @@
|
||||
//
|
||||
// Marker.cs:
|
||||
//
|
||||
// Author:
|
||||
// Ruben Vermeersch (ruben@savanne.be)
|
||||
// Stephane Delcroix (stephane@delcroix.org)
|
||||
//
|
||||
// Copyright (C) 2009 Ruben Vermeersch
|
||||
// Copyright (c) 2009 Stephane Delcroix
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License version
|
||||
// 2.1 as published by the Free Software Foundation.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
//
|
||||
|
||||
namespace TagLib.Jpeg
|
||||
{
|
||||
/// <summary>
|
||||
/// This enum defines the different markers used in JPEG segments.
|
||||
///
|
||||
/// See CCITT Rec. T.81 (1992 E), Table B.1 (p.32)
|
||||
/// </summary>
|
||||
public enum Marker : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Start Of Frame marker, non-differential, Huffman coding, Baseline DCT
|
||||
/// </summary>
|
||||
SOF0 = 0xc0,
|
||||
|
||||
/// <summary>
|
||||
/// Start Of Frame marker, non-differential, Huffman coding, Extended Sequential DCT
|
||||
/// </summary>
|
||||
SOF1,
|
||||
|
||||
/// <summary>
|
||||
/// Start Of Frame marker, non-differential, Huffman coding, Progressive DCT
|
||||
/// </summary>
|
||||
SOF2,
|
||||
|
||||
/// <summary>
|
||||
/// Start Of Frame marker, non-differential, Huffman coding, Lossless (sequential)
|
||||
/// </summary>
|
||||
SOF3,
|
||||
|
||||
/// <summary>
|
||||
/// Start Of Frame marker, differential, Huffman coding, Differential Sequential DCT
|
||||
/// </summary>
|
||||
SOF5 = 0xc5,
|
||||
|
||||
/// <summary>
|
||||
/// Start Of Frame marker, differential, Huffman coding, Differential Progressive DCT
|
||||
/// </summary>
|
||||
SOF6,
|
||||
/// <summary>
|
||||
/// Start Of Frame marker, differential, Huffman coding, Differential Lossless (sequential)
|
||||
/// </summary>
|
||||
SOF7,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPG extensions
|
||||
/// </summary>
|
||||
JPG,
|
||||
|
||||
/// <summary>
|
||||
/// Start Of Frame marker, non-differential, arithmetic coding, Extended Sequential DCT
|
||||
/// </summary>
|
||||
SOF9,
|
||||
|
||||
/// <summary>
|
||||
/// Start Of Frame marker, non-differential, arithmetic coding, Progressive DCT
|
||||
/// </summary>
|
||||
SOF10,
|
||||
|
||||
/// <summary>
|
||||
/// Start Of Frame marker, non-differential, arithmetic coding, Lossless (sequential)
|
||||
/// </summary>
|
||||
SOF11,
|
||||
|
||||
/// <summary>
|
||||
/// Start Of Frame marker, differential, arithmetic coding, Differential Sequential DCT
|
||||
/// </summary>
|
||||
SOF13 = 0xcd,
|
||||
|
||||
/// <summary>
|
||||
/// Start Of Frame marker, differential, arithmetic coding, Differential Progressive DCT
|
||||
/// </summary>
|
||||
SOF14,
|
||||
|
||||
/// <summary>
|
||||
/// Start Of Frame marker, differential, arithmetic coding, Differential Lossless (sequential)
|
||||
/// </summary>
|
||||
SOF15,
|
||||
|
||||
/// <summary>
|
||||
/// Define Huffman table(s)
|
||||
/// </summary>
|
||||
DHT = 0xc4,
|
||||
|
||||
/// <summary>
|
||||
/// Define arithmetic coding conditioning(s)
|
||||
/// </summary>
|
||||
DAC = 0xcc,
|
||||
|
||||
//Restart interval termination with modulo 8 count "m"
|
||||
/// <summary>
|
||||
/// Restart
|
||||
/// </summary>
|
||||
RST0 = 0xd0,
|
||||
|
||||
/// <summary>
|
||||
/// Restart
|
||||
/// </summary>
|
||||
RST1,
|
||||
|
||||
/// <summary>
|
||||
/// Restart
|
||||
/// </summary>
|
||||
RST2,
|
||||
|
||||
/// <summary>
|
||||
/// Restart
|
||||
/// </summary>
|
||||
RST3,
|
||||
|
||||
/// <summary>
|
||||
/// Restart
|
||||
/// </summary>
|
||||
RST4,
|
||||
|
||||
/// <summary>
|
||||
/// Restart
|
||||
/// </summary>
|
||||
RST5,
|
||||
|
||||
/// <summary>
|
||||
/// Restart
|
||||
/// </summary>
|
||||
RST6,
|
||||
|
||||
/// <summary>
|
||||
/// Restart
|
||||
/// </summary>
|
||||
RST7,
|
||||
|
||||
/// <summary>
|
||||
/// Start of Image
|
||||
/// </summary>
|
||||
SOI = 0xd8,
|
||||
|
||||
/// <summary>
|
||||
/// End of Image
|
||||
/// </summary>
|
||||
EOI,
|
||||
|
||||
/// <summary>
|
||||
/// Start of scan
|
||||
/// </summary>
|
||||
SOS,
|
||||
|
||||
/// <summary>
|
||||
/// Define quantization table (s)
|
||||
/// </summary>
|
||||
DQT,
|
||||
|
||||
/// <summary>
|
||||
/// Define number of lines
|
||||
/// </summary>
|
||||
DNL,
|
||||
|
||||
/// <summary>
|
||||
/// Define restart interval
|
||||
/// </summary>
|
||||
DRI,
|
||||
|
||||
/// <summary>
|
||||
/// Define hierarchical progression
|
||||
/// </summary>
|
||||
DHP,
|
||||
|
||||
/// <summary>
|
||||
/// Define reference component
|
||||
/// </summary>
|
||||
EXP,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP0 = 0xe0,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP1,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP2,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP3,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP4,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP5,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP6,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP7,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP8,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP9,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP10,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP11,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP12,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP13,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP14,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for application segment
|
||||
/// </summary>
|
||||
APP15,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG0 = 0xf0,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG1,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG2,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG3,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG4,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG5,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG6,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG7,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG8,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG9,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG10,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG11,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG12,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for JPEG extension
|
||||
/// </summary>
|
||||
JPG13,
|
||||
|
||||
/// <summary>
|
||||
/// Comment
|
||||
/// </summary>
|
||||
COM = 0xfe,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 988d640975f7b0ecdbd6790755fa400c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// TagLib.Jpeg.Table.cs:
|
||||
//
|
||||
// Author:
|
||||
// Stephane Delcroix (stephane@delcroix.org)
|
||||
//
|
||||
// Copyright (c) 2009 Stephane Delcroix
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License version
|
||||
// 2.1 as published by the Free Software Foundation.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA
|
||||
//
|
||||
|
||||
namespace TagLib.Jpeg
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains static predefined tables and helpers
|
||||
/// </summary>
|
||||
public static class Table
|
||||
{
|
||||
/// <summary>
|
||||
/// Standard Luminance Quantization table
|
||||
///
|
||||
/// See CCIT Rec. T.81 (1992 E), K.1 (p143)
|
||||
/// </summary>
|
||||
public static int[] StandardLuminanceQuantization = {
|
||||
16, 11, 12, 14, 12, 10, 16, 14,
|
||||
13, 14, 18, 17, 16, 19, 24, 40,
|
||||
26, 24, 22, 22, 24, 49, 35, 37,
|
||||
29, 40, 58, 51, 61, 60, 57, 51,
|
||||
56, 55, 64, 72, 92, 78, 64, 68,
|
||||
87, 69, 55, 56, 80, 109, 81, 87,
|
||||
95, 98, 103, 104, 103, 62, 77, 113,
|
||||
121, 112, 100, 120, 92, 101, 103, 99
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Standard Chrominance Quantization table
|
||||
///
|
||||
/// See CCIT Rec. T.81 (1992 E), K.1 (p143)
|
||||
/// </summary>
|
||||
public static int[] StandardChrominanceQuantization = {
|
||||
17, 18, 18, 24, 21, 24, 47, 26,
|
||||
26, 47, 99, 66, 56, 66, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c45a026681f9443f88ef57834108eedd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in new issue
Block a user