Пример #1
0
        private void ProcessColorImage(Surface fullSurface)
        {
            CICPColorData?colorConversionInfo = null;

            if (this.colorInfoBox is NclxColorInformation nclxColorInformation)
            {
                colorConversionInfo = new CICPColorData
                {
                    colorPrimaries          = nclxColorInformation.ColorPrimaries,
                    transferCharacteristics = nclxColorInformation.TransferCharacteristics,
                    matrixCoefficients      = nclxColorInformation.MatrixCoefficients,
                    fullRange = nclxColorInformation.FullRange
                };
            }

            if (this.colorGridInfo != null)
            {
                FillColorImageGrid(colorConversionInfo, fullSurface);
            }
            else
            {
                DecodeInfo decodeInfo = new DecodeInfo
                {
                    tileColumnIndex = 0,
                    tileRowIndex    = 0,
                    expectedWidth   = (uint)fullSurface.Width,
                    expectedHeight  = (uint)fullSurface.Height
                };

                DecodeColorImage(this.primaryItemId, decodeInfo, colorConversionInfo, fullSurface);
                SetImageColorData(colorConversionInfo, decodeInfo);
            }
        }
Пример #2
0
        public static string TrySerialize(CICPColorData cicpColor)
        {
            // The identity matrix coefficient is never serialized.
            if (cicpColor.matrixCoefficients == CICPMatrixCoefficients.Identity)
            {
                return(null);
            }

            if (cicpColor.colorPrimaries == CICPColorPrimaries.Unspecified ||
                cicpColor.transferCharacteristics == CICPTransferCharacteristics.Unspecified ||
                cicpColor.matrixCoefficients == CICPMatrixCoefficients.Unspecified)
            {
                return(null);
            }

            ushort colorPrimaries          = (ushort)cicpColor.colorPrimaries;
            ushort transferCharacteristics = (ushort)cicpColor.transferCharacteristics;
            ushort matrixCoefficients      = (ushort)cicpColor.matrixCoefficients;

            return(string.Format(CultureInfo.InvariantCulture,
                                 "<CICP {0}=\"{1}\" {2}=\"{3}\" {4}=\"{5}\"/>",
                                 ColorPrimariesPropertyName,
                                 colorPrimaries.ToString(CultureInfo.InvariantCulture),
                                 TransferCharacteristicsPropertyName,
                                 transferCharacteristics.ToString(CultureInfo.InvariantCulture),
                                 MatrixCoefficientsPropertyName,
                                 matrixCoefficients.ToString(CultureInfo.InvariantCulture)));
        }
Пример #3
0
        public static void CompressWithoutTransparency(Surface surface,
                                                       EncoderOptions options,
                                                       AvifProgressCallback avifProgress,
                                                       ref uint progressDone,
                                                       uint progressTotal,
                                                       CICPColorData colorInfo,
                                                       out CompressedAV1Image color)
        {
            BitmapData bitmapData = new BitmapData
            {
                scan0  = surface.Scan0.Pointer,
                width  = (uint)surface.Width,
                height = (uint)surface.Height,
                stride = (uint)surface.Stride
            };

            ProgressContext progressContext = new ProgressContext(avifProgress, progressDone, progressTotal);

            using (CompressedAV1DataAllocator allocator = new CompressedAV1DataAllocator(1))
            {
                IntPtr colorImage;

                CompressedAV1OutputAlloc outputAllocDelegate = new CompressedAV1OutputAlloc(allocator.Allocate);
                EncoderStatus            status = EncoderStatus.Ok;

                if (IntPtr.Size == 8)
                {
                    status = AvifNative_64.CompressImage(ref bitmapData,
                                                         options,
                                                         progressContext,
                                                         ref colorInfo,
                                                         outputAllocDelegate,
                                                         out colorImage,
                                                         IntPtr.Zero);
                }
                else
                {
                    status = AvifNative_86.CompressImage(ref bitmapData,
                                                         options,
                                                         progressContext,
                                                         ref colorInfo,
                                                         outputAllocDelegate,
                                                         out colorImage,
                                                         IntPtr.Zero);
                }

                GC.KeepAlive(outputAllocDelegate);

                if (status != EncoderStatus.Ok)
                {
                    HandleError(status, allocator.ExceptionInfo);
                }

                color = new CompressedAV1Image(allocator.GetCompressedAV1Data(colorImage), surface.Width, surface.Height, options.yuvFormat);
            }

            progressDone = progressContext.progressDone;
            GC.KeepAlive(avifProgress);
        }
Пример #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
        public static void DecompressColor(AvifItemData colorImage,
                                           CICPColorData?colorConversionInfo,
                                           DecodeInfo decodeInfo,
                                           Surface fullSurface)
        {
            if (colorImage is null)
            {
                ExceptionUtil.ThrowArgumentNullException(nameof(colorImage));
            }

            if (decodeInfo is null)
            {
                ExceptionUtil.ThrowArgumentNullException(nameof(decodeInfo));
            }

            if (fullSurface is null)
            {
                ExceptionUtil.ThrowArgumentNullException(nameof(fullSurface));
            }


            DecoderStatus status = DecoderStatus.Ok;

            unsafe
            {
                colorImage.UseBufferPointer((ptr, length) =>
                {
                    BitmapData bitmapData = new BitmapData
                    {
                        scan0  = fullSurface.Scan0.Pointer,
                        width  = (uint)fullSurface.Width,
                        height = (uint)fullSurface.Height,
                        stride = (uint)fullSurface.Stride
                    };
                    UIntPtr colorImageSize = new UIntPtr(length);

                    if (colorConversionInfo.HasValue)
                    {
                        CICPColorData colorData = colorConversionInfo.Value;

                        if (IntPtr.Size == 8)
                        {
                            status = AvifNative_64.DecompressColorImage(ptr,
                                                                        colorImageSize,
                                                                        ref colorData,
                                                                        decodeInfo,
                                                                        ref bitmapData);
                        }
                        else
                        {
                            status = AvifNative_86.DecompressColorImage(ptr,
                                                                        colorImageSize,
                                                                        ref colorData,
                                                                        decodeInfo,
                                                                        ref bitmapData);
                        }
                    }
                    else
                    {
                        if (IntPtr.Size == 8)
                        {
                            status = AvifNative_64.DecompressColorImage(ptr,
                                                                        colorImageSize,
                                                                        IntPtr.Zero,
                                                                        decodeInfo,
                                                                        ref bitmapData);
                        }
                        else
                        {
                            status = AvifNative_86.DecompressColorImage(ptr,
                                                                        colorImageSize,
                                                                        IntPtr.Zero,
                                                                        decodeInfo,
                                                                        ref bitmapData);
                        }
                    }
                });
            }

            if (status != DecoderStatus.Ok)
            {
                HandleError(status);
            }
        }