public static void CreateZipFromDirectory(string pathToDir, string zipName = null, CompressionSpeed mode = CompressionSpeed.Optimal) { if (!Directory.Exists(pathToDir)) { MessageBox.Show($"{pathToDir} doesn't exist!", "Folder", MessageBoxButton.OK, MessageBoxImage.Error); } if (string.IsNullOrEmpty(zipName)) { zipName = new DirectoryInfo(pathToDir).Name; } try { ZipFile.CreateFromDirectory( pathToDir, $@"{Path.GetDirectoryName(pathToDir)}\{zipName}.zip", (CompressionLevel)mode, false); } catch (IOException e) { Debug.WriteLine($"Source - {e.Source}\nMessage - {e.Message}"); } catch (Exception e) { MessageBox.Show($@"{e.Message}", @"Compression failed", MessageBoxButton.OK, MessageBoxImage.Error); } }
/// <summary> /// Saves a document to a stream respecting the properties /// </summary> protected override void OnSaveT(Document input, Stream output, PropertyBasedSaveConfigToken token, Surface scratchSurface, ProgressEventHandler progressCallback) { int quality = token.GetProperty <Int32Property>(PropertyNames.Quality).Value; CompressionSpeed compressionSpeed = (CompressionSpeed)token.GetProperty(PropertyNames.CompressionSpeed).Value; YUVChromaSubsampling chromaSubsampling = (YUVChromaSubsampling)token.GetProperty(PropertyNames.YUVChromaSubsampling).Value; bool preserveExistingTileSize = token.GetProperty <BooleanProperty>(PropertyNames.PreserveExistingTileSize).Value; AvifFile.Save(input, output, quality, compressionSpeed, chromaSubsampling, preserveExistingTileSize, this.maxEncoderThreadsOverride, scratchSurface, progressCallback, this.byteArrayPool.Value); }
private static ImageGridMetadata TryGetImageGridMetadata( Document document, CompressionSpeed compressionSpeed, YUVChromaSubsampling yuvFormat, bool preserveExistingTileSize) { ImageGridMetadata metadata = null; // The VerySlow compression speed always encodes the image as a single tile. if (compressionSpeed != CompressionSpeed.VerySlow) { // The image must have an even size to be eligible for tiling. if ((document.Width & 1) == 0 && (document.Height & 1) == 0) { if (preserveExistingTileSize) { string value = document.Metadata.GetUserValue(ImageGridName); if (!string.IsNullOrEmpty(value)) { ImageGridMetadata serializedData = ImageGridMetadata.TryDeserialize(value); if (serializedData != null && serializedData.IsValidForImage((uint)document.Width, (uint)document.Height, yuvFormat)) { metadata = serializedData; } } } if (metadata is null) { metadata = TryCalculateBestTileSize(document, compressionSpeed); } } } return(metadata); }
public static void Save(Document document, Stream output, int quality, CompressionSpeed compressionSpeed, YUVChromaSubsampling chromaSubsampling, bool preserveExistingTileSize, int?maxEncoderThreadsOverride, Surface scratchSurface, ProgressEventHandler progressCallback, IByteArrayPool arrayPool) { using (RenderArgs args = new RenderArgs(scratchSurface)) { document.Render(args, true); } bool grayscale = IsGrayscaleImage(scratchSurface); AvifMetadata metadata = CreateAvifMetadata(document); EncoderOptions options = new EncoderOptions { quality = quality, compressionSpeed = compressionSpeed, // YUV 4:0:0 is always used for gray-scale images because it // produces the smallest file size with no quality loss. yuvFormat = grayscale ? YUVChromaSubsampling.Subsampling400 : chromaSubsampling, maxThreads = maxEncoderThreadsOverride ?? Environment.ProcessorCount }; // Use BT.709 with sRGB transfer characteristics as the default. CICPColorData colorConversionInfo = new CICPColorData { colorPrimaries = CICPColorPrimaries.BT709, transferCharacteristics = CICPTransferCharacteristics.Srgb, matrixCoefficients = CICPMatrixCoefficients.BT709, fullRange = true }; if (quality == 100 && !grayscale) { // The Identity matrix coefficient places the RGB values into the YUV planes without any conversion. // This reduces the compression efficiency, but allows for fully lossless encoding. options.yuvFormat = YUVChromaSubsampling.IdentityMatrix; // These CICP color values are from the AV1 Bitstream & Decoding Process Specification. colorConversionInfo = new CICPColorData { colorPrimaries = CICPColorPrimaries.BT709, transferCharacteristics = CICPTransferCharacteristics.Srgb, matrixCoefficients = CICPMatrixCoefficients.Identity, fullRange = true }; } else { Metadata docMetadata = document.Metadata; // Look for NCLX meta-data if the CICP meta-data was not found. // This preserves backwards compatibility with PDN files created by // previous versions of this plugin. string serializedData = docMetadata.GetUserValue(CICPMetadataName) ?? docMetadata.GetUserValue(NclxMetadataName); if (serializedData != null) { CICPColorData?colorData = CICPSerializer.TryDeserialize(serializedData); if (colorData.HasValue) { colorConversionInfo = colorData.Value; } } } ImageGridMetadata imageGridMetadata = TryGetImageGridMetadata(document, options.compressionSpeed, options.yuvFormat, preserveExistingTileSize); bool hasTransparency = HasTransparency(scratchSurface); CompressedAV1ImageCollection colorImages = new CompressedAV1ImageCollection(imageGridMetadata?.TileCount ?? 1); CompressedAV1ImageCollection alphaImages = hasTransparency ? new CompressedAV1ImageCollection(colorImages.Capacity) : null; // Progress is reported at the following stages: // 1. Before converting the image to the YUV color space // 2. Before compressing the color image // 3. After compressing the color image // 4. After compressing the alpha image (if present) // 5. After writing the color image to the file // 6. After writing the alpha image to the file (if present) uint progressDone = 0; uint progressTotal = hasTransparency ? 6U : 4U; if (colorImages.Capacity > 1) { progressTotal *= (uint)colorImages.Capacity; } try { Rectangle[] windowRectangles = GetTileWindowRectangles(imageGridMetadata, document); for (int i = 0; i < colorImages.Capacity; i++) { CompressedAV1Image color = null; CompressedAV1Image alpha = null; try { Rectangle windowRect = windowRectangles[i]; using (Surface window = scratchSurface.CreateWindow(windowRect)) { if (hasTransparency) { AvifNative.CompressWithTransparency(window, options, ReportCompressionProgress, ref progressDone, progressTotal, colorConversionInfo, out color, out alpha); } else { AvifNative.CompressWithoutTransparency(window, options, ReportCompressionProgress, ref progressDone, progressTotal, colorConversionInfo, out color); } } colorImages.Add(color); color = null; if (hasTransparency) { alphaImages.Add(alpha); alpha = null; } } finally { color?.Dispose(); alpha?.Dispose(); } } ColorInformationBox colorInformationBox; byte[] iccProfileBytes = metadata.GetICCProfileBytesReadOnly(); if (iccProfileBytes != null && iccProfileBytes.Length > 0) { colorInformationBox = new IccProfileColorInformation(iccProfileBytes); } else { colorInformationBox = new NclxColorInformation(colorConversionInfo.colorPrimaries, colorConversionInfo.transferCharacteristics, colorConversionInfo.matrixCoefficients, colorConversionInfo.fullRange); } AvifWriter writer = new AvifWriter(colorImages, alphaImages, metadata, imageGridMetadata, options.yuvFormat, colorInformationBox, progressCallback, progressDone, progressTotal, arrayPool); writer.WriteTo(output); } finally { colorImages?.Dispose(); alphaImages?.Dispose(); } bool ReportCompressionProgress(uint done, uint total) { try { progressCallback?.Invoke(null, new ProgressEventArgs(((double)done / total) * 100.0, true)); return(true); } catch (OperationCanceledException) { return(false); } } }
private static ImageGridMetadata TryCalculateBestTileSize( Document document, CompressionSpeed compressionSpeed) { // Although the HEIF specification (ISO/IEC 23008-12:2017) allows an image grid to have up to 256 tiles // in each direction (65536 total), the ISO base media file format (ISO/IEC 14496-12:2015) limits // an item reference box to 65535 items. // Because of this we limit the maximum number of tiles to 250. // // While this would result in the image using 62500 tiles in the worst case, it allows // memory usage to be minimized when encoding extremely wide and/or tall images. // // For example, a 65536x65536 pixel image would use a 128x128 grid of 512x512 pixel tiles. const int MaxTileCount = 250; // The MIAF specification (ISO/IEC 23000-22:2019) requires that the tile size be at least 64x64 pixels. const int MinTileSize = 64; int maxTileSize; switch (compressionSpeed) { case CompressionSpeed.Fast: maxTileSize = 512; break; case CompressionSpeed.Medium: maxTileSize = 1280; break; case CompressionSpeed.Slow: maxTileSize = 1920; break; case CompressionSpeed.VerySlow: // Tiles are not used for the very slow compression speed. return(null); default: throw new InvalidEnumArgumentException(nameof(compressionSpeed), (int)compressionSpeed, typeof(CompressionSpeed)); } int bestTileColumnCount = 1; int bestTileWidth = document.Width; int bestTileRowCount = 1; int bestTileHeight = document.Height; if (document.Width > maxTileSize) { for (int tileColumnCount = 2; tileColumnCount <= MaxTileCount; tileColumnCount++) { int tileWidth = document.Width / tileColumnCount; if (tileWidth < MinTileSize) { break; } if ((tileWidth & 1) == 0 && (tileWidth * tileColumnCount) == document.Width) { bestTileWidth = tileWidth; bestTileColumnCount = tileColumnCount; if (tileWidth <= maxTileSize) { break; } } } } if (document.Height > maxTileSize) { if (document.Width == document.Height) { // Square images use the same number of horizontal and vertical tiles. bestTileHeight = bestTileWidth; bestTileRowCount = bestTileColumnCount; } else { for (int tileRowCount = 2; tileRowCount <= MaxTileCount; tileRowCount++) { int tileHeight = document.Height / tileRowCount; if (tileHeight < MinTileSize) { break; } if ((tileHeight & 1) == 0 && (tileHeight * tileRowCount) == document.Height) { bestTileHeight = tileHeight; bestTileRowCount = tileRowCount; if (tileHeight <= maxTileSize) { break; } } } } } ImageGridMetadata metadata = null; if (bestTileColumnCount > 1 || bestTileRowCount > 1) { metadata = new ImageGridMetadata(bestTileColumnCount, bestTileRowCount, (uint)document.Height, (uint)document.Width, (uint)bestTileHeight, (uint)bestTileWidth); } return(metadata); }