src
HeifEncoder.cs
/*
* .NET bindings for libheif.
* Copyright (c) 2020, 2021 Nicholas Hayes
*
* Portions Copyright (c) 2017 struktur AG, Dirk Farin
*
* This file is part of libheif-sharp.
*
* libheif-sharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libheif-sharp 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 libheif-sharp. If not, see .
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using LibHeifSharp.Interop;
using LibHeifSharp.Properties;
using LibHeifSharp.ResourceManagement;
namespace LibHeifSharp
{
///
/// A LibHeif encoder instance
///
///
///
public sealed clast HeifEncoder : Disposable
{
private SafeHeifEncoder encoder;
private bool lossyQualitySet;
private readonly Lazy encoderParameterList;
private readonly Lazy encoderParameterTypes;
///
/// Initializes a new instance of the clast.
///
/// The encoder.
internal HeifEncoder(SafeHeifEncoder encoder)
{
Validate.IsNotNull(encoder, nameof(encoder));
this.encoder = encoder;
this.lossyQualitySet = false;
this.encoderParameterList = new Lazy(GetEncoderParameterList);
this.encoderParameterTypes = new Lazy(GetEncoderParameterTypes);
}
///
/// Gets a list of the supported encoder parameters.
///
///
/// A list of the supported encoder parameters.
///
/// An error occurred when creating the encoder parameters list.
/// The object has been disposed.
public IReadOnlyList EncoderParameters
{
get
{
VerifyNotDisposed();
return this.encoderParameterList.Value;
}
}
///
/// Gets the encoder handle.
///
///
/// The encoder handle.
///
/// The object has been disposed.
internal SafeHeifEncoder SafeHeifEncoder
{
get
{
VerifyNotDisposed();
return this.encoder;
}
}
///
/// Sets the encoder lossy quality.
///
/// The quality value.
/// must be in the range of [0, 100] inclusive.
/// A LibHeif error occurred.
/// The object has been disposed.
public void SetLossyQuality(int quality)
{
if (quality < 0 || quality > 100)
{
ExceptionUtil.ThrowArgumentOutOfRangeException(nameof(quality), "Must be in the range of [0, 100].");
}
VerifyNotDisposed();
var error = LibHeifNative.heif_encoder_set_lossy_quality(this.encoder, quality);
error.ThrowIfError();
this.lossyQualitySet = true;
}
///
/// Sets the lossless compression support.
///
///
/// if the encoder should use lossless compression; otherwise, .
///
/// A LibHeif error occurred.
/// The object has been disposed.
public void SetLossless(bool lossless)
{
VerifyNotDisposed();
var error = LibHeifNative.heif_encoder_set_lossless(this.encoder, lossless);
error.ThrowIfError();
if (lossless && !this.lossyQualitySet)
{
// LibHeif requires the lossy quality to be always be set, if it has
// not been set the encoder will produce a corrupted image.
error = LibHeifNative.heif_encoder_set_lossy_quality(this.encoder, 50);
error.ThrowIfError();
this.lossyQualitySet = true;
}
}
///
/// Sets an encoder parameter.
///
/// The parameter.
/// The parameter value.
/// is null.
/// A LibHeif error occurred.
/// The object has been disposed.
public void SetParameter(HeifBooleanEncoderParameter parameter, bool value)
{
Validate.IsNotNull(parameter, nameof(parameter));
SetParameter(parameter.Name, value);
}
///
/// Sets an encoder parameter.
///
/// The parameter.
/// The parameter value.
/// is null.
/// A LibHeif error occurred.
/// The object has been disposed.
public void SetParameter(HeifIntegerEncoderParameter parameter, int value)
{
Validate.IsNotNull(parameter, nameof(parameter));
SetParameter(parameter.Name, value);
}
///
/// Sets an encoder parameter.
///
/// The parameter.
/// The parameter value.
///
/// is null.
///
/// -or-
///
/// is null.
///
/// A LibHeif error occurred.
/// The object has been disposed.
public void SetParameter(HeifStringEncoderParameter parameter, string value)
{
Validate.IsNotNull(parameter, nameof(parameter));
SetStringParameter(parameter.Name, value, coerceParameterType: false);
}
///
/// Sets an encoder parameter.
///
/// The parameter name.
/// The parameter value.
/// is null.
/// A LibHeif error occurred.
/// The object has been disposed.
public void SetParameter(string name, bool value)
{
Validate.IsNotNull(name, nameof(name));
VerifyNotDisposed();
SetBooleanParameter(name, value);
}
///
/// Sets an encoder parameter.
///
/// The parameter name.
/// The parameter value.
/// is null.
/// A LibHeif error occurred.
/// The object has been disposed.
public void SetParameter(string name, int value)
{
Validate.IsNotNull(name, nameof(name));
VerifyNotDisposed();
SetIntegerParameter(name, value);
}
///
/// Sets an encoder parameter.
///
/// The parameter name.
/// The parameter value.
///
/// is null.
///
/// -or-
///
/// is null.
///
///
/// A LibHeif error occurred.
///
/// -or-
///
/// Unable to convert the parameter to the correct type.
///
/// The object has been disposed.
public void SetParameter(string name, string value)
{
SetStringParameter(name, value, coerceParameterType: true);
}
///
/// Releases unmanaged and - optionally - managed resources.
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected override void Dispose(bool disposing)
{
if (disposing)
{
DisposableUtil.Free(ref this.encoder);
}
base.Dispose(disposing);
}
///
/// Gets the encoder parameter list.
///
/// The encoder parameter list.
/// An error occurred when creating the encoder parameter.
private unsafe ReadOnlyCollection GetEncoderParameterList()
{
var encoderParameters = new List();
var parameterList = LibHeifNative.heif_encoder_list_parameters(this.encoder);
if (parameterList.Value != IntPtr.Zero)
{
var encoderParameter = (heif_encoder_parameter*)parameterList.Value;
while (*encoderParameter != heif_encoder_parameter.Null)
{
encoderParameters.Add(HeifEncoderParameterFactory.Create(this.encoder, *encoderParameter));
encoderParameter++;
}
}
return encoderParameters.AsReadOnly();
}
///
/// Gets the encoder parameter type dictionary.
///
/// The encoder parameter type dictionary.
/// An error occurred when creating the encoder parameter.
private IReadOnlyDictionary GetEncoderParameterTypes()
{
var encoderParameterList = this.encoderParameterList.Value;
var parameterTypes = new Dictionary(encoderParameterList.Count,
StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < encoderParameterList.Count; i++)
{
var parameter = encoderParameterList[i];
parameterTypes.Add(parameter.Name, parameter.ParameterType);
}
return parameterTypes;
}
///
/// Sets a Boolean encoder parameter.
///
/// The parameter name.
/// The parameter value.
/// A LibHeif error occurred.
private void SetBooleanParameter(string name, bool value)
{
var error = LibHeifNative.heif_encoder_set_parameter_boolean(this.encoder, name, value);
error.ThrowIfError();
}
///
/// Sets an integer encoder parameter.
///
/// The parameter name.
/// The parameter value.
/// A LibHeif error occurred.
private void SetIntegerParameter(string name, int value)
{
var error = LibHeifNative.heif_encoder_set_parameter_integer(this.encoder, name, value);
error.ThrowIfError();
}
///
/// Sets a string encoder parameter.
///
/// The parameter name.
/// The parameter value.
///
/// if the parameter should be coerced to the correct type; otherwise, .
///
///
/// is null.
///
/// -or-
///
/// is null.
///
///
/// A LibHeif error occurred.
///
/// -or-
///
/// Unable to convert the parameter to the correct type.
///
/// The object has been disposed.
private void SetStringParameter(string name, string value, bool coerceParameterType)
{
Validate.IsNotNull(name, nameof(name));
Validate.IsNotNull(value, nameof(value));
VerifyNotDisposed();
if (coerceParameterType)
{
// Some encoders expect the method that is called to match the parameter type.
// Attempting to set a Boolean or Integer parameter as a string will cause an invalid parameter error.
if (this.encoderParameterTypes.Value.TryGetValue(name, out var type))
{
switch (type)
{
case HeifEncoderParameterType.Boolean:
if (TryConvertStringToBoolean(value, out bool boolValue))
{
SetBooleanParameter(name, boolValue);
}
else
{
throw new HeifException(string.Format(CultureInfo.CurrentCulture,
Resources.CoerceStringValueToBooleanFailedFormat,
nameof(value)));
}
return;
case HeifEncoderParameterType.Integer:
if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int intValue))
{
SetIntegerParameter(name, intValue);
}
else
{
throw new HeifException(string.Format(CultureInfo.CurrentCulture,
Resources.CoerceStringValueToIntegerFailedFormat,
nameof(value)));
}
return;
case HeifEncoderParameterType.String:
// The parameter is the correct type.
break;
default:
throw new HeifException($"Unknown { nameof(HeifEncoderParameterType) }: { type }.");
}
}
}
var error = LibHeifNative.heif_encoder_set_parameter_string(this.encoder, name, value);
error.ThrowIfError();
}
private static bool TryConvertStringToBoolean(string value, out bool result)
{
if (!string.IsNullOrWhiteSpace(value))
{
if (bool.TryParse(value, out result))
{
return true;
}
else if (value.Equals("1", StringComparison.Ordinal))
{
result = true;
return true;
}
else if (value.Equals("0", StringComparison.Ordinal))
{
result = false;
return true;
}
}
result = false;
return false;
}
}
}