mirror of https://github.com/SixLabors/ImageSharp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
5.8 KiB
197 lines
5.8 KiB
// <copyright file="IccProfile.cs" company="James Jackson-South">
|
|
// Copyright (c) James Jackson-South and contributors.
|
|
// Licensed under the Apache License, Version 2.0.
|
|
// </copyright>
|
|
|
|
namespace ImageSharp
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
#if !NETSTANDARD1_1
|
|
using System.Security.Cryptography;
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Represents an ICC profile
|
|
/// </summary>
|
|
public sealed class IccProfile
|
|
{
|
|
/// <summary>
|
|
/// The byte array to read the ICC profile from
|
|
/// </summary>
|
|
private byte[] data;
|
|
|
|
/// <summary>
|
|
/// The backing file for the <see cref="Entries"/> property
|
|
/// </summary>
|
|
private List<IccTagDataEntry> entries;
|
|
|
|
/// <summary>
|
|
/// ICC profile header
|
|
/// </summary>
|
|
private IccProfileHeader header;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="IccProfile"/> class.
|
|
/// </summary>
|
|
public IccProfile()
|
|
: this((byte[])null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="IccProfile"/> class.
|
|
/// </summary>
|
|
/// <param name="data">The raw ICC profile data</param>
|
|
public IccProfile(byte[] data)
|
|
{
|
|
this.data = data;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="IccProfile"/> class
|
|
/// by making a copy from another ICC profile.
|
|
/// </summary>
|
|
/// <param name="other">The other ICC profile, where the clone should be made from.</param>
|
|
/// <exception cref="System.ArgumentNullException"><paramref name="other"/> is null.</exception>>
|
|
public IccProfile(IccProfile other)
|
|
{
|
|
Guard.NotNull(other, nameof(other));
|
|
|
|
// TODO: Do we need to copy anything else?
|
|
if (other.data != null)
|
|
{
|
|
this.data = new byte[other.data.Length];
|
|
Buffer.BlockCopy(other.data, 0, this.data, 0, other.data.Length);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="IccProfile"/> class.
|
|
/// </summary>
|
|
/// <param name="header">The profile header</param>
|
|
/// <param name="entries">The actual profile data</param>
|
|
internal IccProfile(IccProfileHeader header, IEnumerable<IccTagDataEntry> entries)
|
|
{
|
|
Guard.NotNull(header, nameof(header));
|
|
Guard.NotNull(entries, nameof(entries));
|
|
|
|
this.header = header;
|
|
this.entries = new List<IccTagDataEntry>(entries);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the profile header
|
|
/// </summary>
|
|
public IccProfileHeader Header
|
|
{
|
|
get
|
|
{
|
|
this.InitializeHeader();
|
|
return this.header;
|
|
}
|
|
|
|
set => this.header = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the actual profile data
|
|
/// </summary>
|
|
public List<IccTagDataEntry> Entries
|
|
{
|
|
get
|
|
{
|
|
this.InitializeEntries();
|
|
return this.entries;
|
|
}
|
|
}
|
|
|
|
#if !NETSTANDARD1_1
|
|
|
|
/// <summary>
|
|
/// Calculates the MD5 hash value of an ICC profile header
|
|
/// </summary>
|
|
/// <param name="data">The data of which to calculate the hash value</param>
|
|
/// <returns>The calculated hash</returns>
|
|
public static IccProfileId CalculateHash(byte[] data)
|
|
{
|
|
Guard.NotNull(data, nameof(data));
|
|
Guard.IsTrue(data.Length >= 128, nameof(data), "Data length must be at least 128 to be a valid profile header");
|
|
|
|
byte[] header = new byte[128];
|
|
Buffer.BlockCopy(data, 0, header, 0, 128);
|
|
|
|
using (var md5 = MD5.Create())
|
|
{
|
|
// Zero out some values
|
|
Array.Clear(header, 44, 4); // Profile flags
|
|
Array.Clear(header, 64, 4); // Rendering Intent
|
|
Array.Clear(header, 84, 16); // Profile ID
|
|
|
|
// Calculate hash
|
|
byte[] hash = md5.ComputeHash(data);
|
|
|
|
// Read values from hash
|
|
var reader = new IccDataReader(hash);
|
|
return reader.ReadProfileId();
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Extends the profile with additional data.
|
|
/// </summary>
|
|
/// <param name="bytes">The array containing addition profile data.</param>
|
|
public void Extend(byte[] bytes)
|
|
{
|
|
int currentLength = this.data.Length;
|
|
Array.Resize(ref this.data, currentLength + bytes.Length);
|
|
Buffer.BlockCopy(bytes, 0, this.data, currentLength, bytes.Length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts this instance to a byte array.
|
|
/// </summary>
|
|
/// <returns>The <see cref="T:byte[]"/></returns>
|
|
public byte[] ToByteArray()
|
|
{
|
|
var writer = new IccWriter();
|
|
return writer.Write(this);
|
|
}
|
|
|
|
private void InitializeHeader()
|
|
{
|
|
if (this.header != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (this.data == null)
|
|
{
|
|
this.header = new IccProfileHeader();
|
|
return;
|
|
}
|
|
|
|
var reader = new IccReader();
|
|
this.header = reader.ReadHeader(this.data);
|
|
}
|
|
|
|
private void InitializeEntries()
|
|
{
|
|
if (this.entries != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (this.data == null)
|
|
{
|
|
this.entries = new List<IccTagDataEntry>();
|
|
return;
|
|
}
|
|
|
|
var reader = new IccReader();
|
|
this.entries = new List<IccTagDataEntry>(reader.ReadTagData(this.data));
|
|
}
|
|
}
|
|
}
|
|
|