コード例 #1
0
ファイル: Compressor.cs プロジェクト: Matixk/SanityArchiver
        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);
            }
        }
コード例 #2
0
        /// <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);
        }
コード例 #3
0
        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);
        }
コード例 #4
0
        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);
                }
            }
        }
コード例 #5
0
        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);
        }