示例#1
0
        public static void AddPlanarCache(PipelineContext ctx)
        {
            using var transform = default(ComPtr <IWICPlanarBitmapSourceTransform>);
            if (ctx.Source is not WicPixelSource wsrc || FAILED(wsrc.WicSource->QueryInterface(__uuidof <IWICPlanarBitmapSourceTransform>(), (void **)transform.GetAddressOf())))
            {
                throw new NotSupportedException("Transform chain doesn't support planar mode.  Only JPEG Decoder, Rotator, Scaler, and PixelFormatConverter are allowed");
            }

            int  ratio = ctx.Settings.HybridScaleRatio.Clamp(1, 8);
            uint ow = (uint)ctx.Source.Width, oh = (uint)ctx.Source.Height;
            uint cw = (uint)MathUtil.DivCeiling((int)ow, ratio), ch = (uint)MathUtil.DivCeiling((int)oh, ratio);

            var desc = stackalloc WICBitmapPlaneDescription[PlanarPixelFormats.Length];

            fixed(Guid *pfmt = PlanarPixelFormats)
            {
                int bval;

                HRESULT.Check(transform.Get()->DoesSupportTransform(&cw, &ch, WICBitmapTransformOptions.WICBitmapTransformRotate0, WICPlanarOptions.WICPlanarOptionsDefault, pfmt, desc, (uint)PlanarPixelFormats.Length, &bval));
                if (bval == 0)
                {
                    throw new NotSupportedException("Requested planar transform not supported");
                }
            }

            var crop  = PixelArea.FromGdiRect(ctx.Settings.Crop).DeOrient(ctx.Orientation, (int)ow, (int)oh).ProportionalScale((int)ow, (int)oh, (int)cw, (int)ch);
            var cache = ctx.AddDispose(new WicPlanarCache(transform.Detach(), new Span <WICBitmapPlaneDescription>(desc, PlanarPixelFormats.Length), WICBitmapTransformOptions.WICBitmapTransformRotate0, cw, ch, crop));

            ctx.PlanarContext       = new PipelineContext.PlanarPipelineContext(cache.SourceY, cache.SourceCb, cache.SourceCr);
            ctx.Source              = ctx.PlanarContext.SourceY;
            ctx.Settings.Crop       = ctx.Source.Area.ReOrient(ctx.Orientation, ctx.Source.Width, ctx.Source.Height).ToGdiRect();
            ctx.Settings.HybridMode = HybridScaleMode.Off;
        }
示例#2
0
        public static void AddPlanarCache(PipelineContext ctx)
        {
            if (!(ctx.Source.WicSource is IWICPlanarBitmapSourceTransform trans))
            {
                throw new NotSupportedException("Transform chain doesn't support planar mode.  Only JPEG Decoder, Rotator, Scaler, and PixelFormatConverter are allowed");
            }

            int  ratio = ctx.Settings.HybridScaleRatio.Clamp(1, 8);
            uint ow = (uint)ctx.Source.Width, oh = (uint)ctx.Source.Height;
            uint cw = (uint)MathUtil.DivCeiling((int)ow, ratio), ch = (uint)MathUtil.DivCeiling((int)oh, ratio);

            var desc = ArrayPool <WICBitmapPlaneDescription> .Shared.Rent(PlanarPixelFormats.Length);

            if (!trans.DoesSupportTransform(ref cw, ref ch, WICBitmapTransformOptions.WICBitmapTransformRotate0, WICPlanarOptions.WICPlanarOptionsDefault, PlanarPixelFormats, desc, (uint)PlanarPixelFormats.Length))
            {
                throw new NotSupportedException("Requested planar transform not supported");
            }

            var crop  = PixelArea.FromGdiRect(ctx.Settings.Crop).DeOrient(ctx.Orientation, (int)ow, (int)oh).ProportionalScale((int)ow, (int)oh, (int)cw, (int)ch);
            var cache = ctx.AddDispose(new WicPlanarCache(trans, desc, WICBitmapTransformOptions.WICBitmapTransformRotate0, cw, ch, crop));

            ArrayPool <WICBitmapPlaneDescription> .Shared.Return(desc);

            ctx.PlanarContext       = new PipelineContext.PlanarPipelineContext(cache.SourceY, cache.SourceCb, cache.SourceCr);
            ctx.Source              = ctx.PlanarContext.SourceY;
            ctx.Settings.Crop       = ctx.Source.Area.ReOrient(ctx.Orientation, ctx.Source.Width, ctx.Source.Height).ToGdiRect();
            ctx.Settings.HybridMode = HybridScaleMode.Off;
        }
示例#3
0
        public static void AddNativeScaler(PipelineContext ctx)
        {
            int ratio = ctx.Settings.HybridScaleRatio;

            if (ratio == 1 || ctx.ImageFrame is not WicImageFrame wicFrame || !wicFrame.SupportsNativeScale || ctx.Source is not WicPixelSource wsrc)
            {
                return;
            }

            using var transform = default(ComPtr <IWICBitmapSourceTransform>);
            if (FAILED(wsrc.WicSource->QueryInterface(__uuidof <IWICBitmapSourceTransform>(), (void **)transform.GetAddressOf())))
            {
                return;
            }

            uint ow = (uint)ctx.Source.Width, oh = (uint)ctx.Source.Height;
            uint cw = (uint)MathUtil.DivCeiling((int)ow, ratio), ch = (uint)MathUtil.DivCeiling((int)oh, ratio);

            HRESULT.Check(transform.Get()->GetClosestSize(&cw, &ch));

            if (cw == ow && ch == oh)
            {
                return;
            }

            var orient = ctx.Orientation;

            using var scaler = default(ComPtr <IWICBitmapScaler>);
            HRESULT.Check(Wic.Factory->CreateBitmapScaler(scaler.GetAddressOf()));
            HRESULT.Check(scaler.Get()->Initialize(ctx.Source.AsIWICBitmapSource(ctx), cw, ch, WICBitmapInterpolationMode.WICBitmapInterpolationModeFant));

            ctx.Source              = ctx.AddDispose(new ComPtr <IWICBitmapSource>((IWICBitmapSource *)scaler.Get()).AsPixelSource(ctx, nameof(IWICBitmapSourceTransform)));
            ctx.Settings.Crop       = PixelArea.FromGdiRect(ctx.Settings.Crop).DeOrient(orient, (int)ow, (int)oh).ProportionalScale((int)ow, (int)oh, (int)cw, (int)ch).ReOrient(orient, (int)cw, (int)ch).ToGdiRect();
            ctx.Settings.HybridMode = HybridScaleMode.Off;
        }
示例#4
0
        public static void AddNativeScaler(PipelineContext ctx)
        {
            int ratio = ctx.Settings.HybridScaleRatio;

            if (ratio == 1 || !(ctx.ImageFrame is WicImageFrame wicFrame) || !wicFrame.SupportsNativeScale || !(ctx.Source.WicSource is IWICBitmapSourceTransform trans))
            {
                return;
            }

            uint ow = (uint)ctx.Source.Width, oh = (uint)ctx.Source.Height;
            uint cw = (uint)MathUtil.DivCeiling((int)ow, ratio), ch = (uint)MathUtil.DivCeiling((int)oh, ratio);

            trans.GetClosestSize(ref cw, ref ch);

            if (cw == ow && ch == oh)
            {
                return;
            }

            var orient = ctx.Orientation;
            var scaler = ctx.WicContext.AddRef(Wic.Factory.CreateBitmapScaler());

            scaler.Initialize(ctx.Source.WicSource, cw, ch, WICBitmapInterpolationMode.WICBitmapInterpolationModeFant);

            ctx.Source              = scaler.AsPixelSource(nameof(IWICBitmapSourceTransform));
            ctx.Settings.Crop       = PixelArea.FromGdiRect(ctx.Settings.Crop).DeOrient(orient, (int)ow, (int)oh).ProportionalScale((int)ow, (int)oh, (int)cw, (int)ch).ReOrient(orient, (int)cw, (int)ch).ToGdiRect();
            ctx.Settings.HybridMode = HybridScaleMode.Off;
        }
示例#5
0
        public void WriteSource(PipelineContext ctx, PixelArea area = default)
        {
            var wicFrame = WicEncoderFrame;
            var wicRect  = area.ToWicRect();

            if (ctx.PlanarContext is not null)
            {
                var oformat = GUID_WICPixelFormat24bppBGR;
                HRESULT.Check(wicFrame->SetPixelFormat(&oformat));

                using var gchY  = new WeakGCHandle(ctx.PlanarContext.SourceY);
                using var gchCb = new WeakGCHandle(ctx.PlanarContext.SourceCb);
                using var gchCr = new WeakGCHandle(ctx.PlanarContext.SourceCr);

                var srcY   = new IWICBitmapSourceImpl(gchY.Handle);
                var srcCb  = new IWICBitmapSourceImpl(gchCb.Handle);
                var srcCr  = new IWICBitmapSourceImpl(gchCr.Handle);
                var planes = stackalloc[] {&srcY, &srcCb, &srcCr };

                using var pframe = default(ComPtr <IWICPlanarBitmapFrameEncode>);
                HRESULT.Check(wicFrame->QueryInterface(__uuidof <IWICPlanarBitmapFrameEncode>(), (void **)pframe.GetAddressOf()));
                HRESULT.Check(pframe.Get()->WriteSource((IWICBitmapSource **)planes, 3, area.IsEmpty ? null : &wicRect));
            }
            else
            {
                var oformat = ctx.Source.Format.FormatGuid;
                HRESULT.Check(wicFrame->SetPixelFormat(&oformat));
                if (oformat != ctx.Source.Format.FormatGuid)
                {
                    var ptt = WICBitmapPaletteType.WICBitmapPaletteTypeCustom;
                    using var pal = default(ComPtr <IWICPalette>);
                    if (PixelFormat.FromGuid(oformat).NumericRepresentation == PixelNumericRepresentation.Indexed)
                    {
                        HRESULT.Check(Wic.Factory->CreatePalette(pal.GetAddressOf()));
                        HRESULT.Check(pal.Get()->InitializePredefined(WICBitmapPaletteType.WICBitmapPaletteTypeFixedGray256, 0));
                        ptt = WICBitmapPaletteType.WICBitmapPaletteTypeFixedGray256;

                        HRESULT.Check(wicFrame->SetPalette(pal));
                    }

                    using var conv = default(ComPtr <IWICFormatConverter>);
                    HRESULT.Check(Wic.Factory->CreateFormatConverter(conv.GetAddressOf()));
                    HRESULT.Check(conv.Get()->Initialize(ctx.Source.AsIWICBitmapSource(ctx), &oformat, WICBitmapDitherType.WICBitmapDitherTypeNone, pal, 0.0, ptt));

                    ctx.Source = ctx.AddDispose(new ComPtr <IWICBitmapSource>((IWICBitmapSource *)conv.Get()).AsPixelSource(null, $"{nameof(IWICFormatConverter)}: {ctx.Source.Format.Name}->{PixelFormat.FromGuid(oformat).Name}", false));
                }
                else if (oformat == PixelFormat.Indexed8Bpp.FormatGuid)
                {
                    Debug.Assert(ctx.WicContext.DestPalette is not null);

                    HRESULT.Check(wicFrame->SetPalette(ctx.WicContext.DestPalette));
                }

                using var gch = new WeakGCHandle(ctx.Source);
                var src = new IWICBitmapSourceImpl(gch.Handle);
                HRESULT.Check(wicFrame->WriteSource((IWICBitmapSource *)&src, area.IsEmpty ? null : &wicRect));
            }

            HRESULT.Check(wicFrame->Commit());
        }
示例#6
0
        public void WriteSource(PipelineContext ctx, PixelArea area = default)
        {
            var wicFrame = WicEncoderFrame;
            var wicRect  = area.ToWicRect();

            if (ctx.PlanarContext is not null)
            {
                var oformat = Consts.GUID_WICPixelFormat24bppBGR;
                wicFrame.SetPixelFormat(ref oformat);

                var planes = ArrayPool <IWICBitmapSource> .Shared.Rent(3);

                planes[0] = ctx.PlanarContext.SourceY.AsIWICBitmapSource();
                planes[1] = ctx.PlanarContext.SourceCb.AsIWICBitmapSource();
                planes[2] = ctx.PlanarContext.SourceCr.AsIWICBitmapSource();
                ((IWICPlanarBitmapFrameEncode)wicFrame).WriteSource(planes, 3, area.IsEmpty ? ref WICRect.Null : ref wicRect);

                ArrayPool <IWICBitmapSource> .Shared.Return(planes);
            }
            else
            {
                var oformat = ctx.Source.Format.FormatGuid;
                wicFrame.SetPixelFormat(ref oformat);
                if (oformat != ctx.Source.Format.FormatGuid)
                {
                    var pal = default(IWICPalette);
                    var ptt = WICBitmapPaletteType.WICBitmapPaletteTypeCustom;
                    if (PixelFormat.FromGuid(oformat).NumericRepresentation == PixelNumericRepresentation.Indexed)
                    {
                        pal = ctx.WicContext.AddRef(Wic.Factory.CreatePalette());
                        pal.InitializePredefined(WICBitmapPaletteType.WICBitmapPaletteTypeFixedGray256, false);
                        ptt = WICBitmapPaletteType.WICBitmapPaletteTypeFixedGray256;

                        wicFrame.SetPalette(pal);
                    }

                    var conv = ctx.WicContext.AddRef(Wic.Factory.CreateFormatConverter());
                    conv.Initialize(ctx.Source.AsIWICBitmapSource(), oformat, WICBitmapDitherType.WICBitmapDitherTypeNone, pal, 0.0, ptt);
                    ctx.Source = conv.AsPixelSource($"{nameof(IWICFormatConverter)}: {ctx.Source.Format.Name}->{PixelFormat.FromGuid(oformat).Name}", false);
                }
                else if (oformat == PixelFormat.Indexed8Bpp.FormatGuid)
                {
                    Debug.Assert(ctx.WicContext.DestPalette is not null);

                    wicFrame.SetPalette(ctx.WicContext.DestPalette);
                }

                wicFrame.WriteSource(ctx.Source.AsIWICBitmapSource(), area.IsEmpty ? ref WICRect.Null : ref wicRect);
            }

            wicFrame.Commit();
        }
示例#7
0
        public static void AddCropper(PipelineContext ctx)
        {
            var crop = PixelArea.FromGdiRect(ctx.Settings.Crop);

            if (crop == ctx.Source.Area)
            {
                return;
            }

            var cropper = ctx.WicContext.AddRef(Wic.Factory.CreateBitmapClipper());

            cropper.Initialize(ctx.Source.WicSource, crop.ToWicRect());

            ctx.Source        = cropper.AsPixelSource(nameof(IWICBitmapClipper));
            ctx.Settings.Crop = ctx.Source.Area.ToGdiRect();
        }
示例#8
0
        public static void AddCropper(PipelineContext ctx)
        {
            var crop = PixelArea.FromGdiRect(ctx.Settings.Crop);

            if (crop == ctx.Source.Area)
            {
                return;
            }

            var rect = crop.ToWicRect();

            using var cropper = default(ComPtr <IWICBitmapClipper>);
            HRESULT.Check(Wic.Factory->CreateBitmapClipper(cropper.GetAddressOf()));
            HRESULT.Check(cropper.Get()->Initialize(ctx.Source.AsIWICBitmapSource(ctx), &rect));

            ctx.Source        = ctx.AddDispose(new ComPtr <IWICBitmapSource>((IWICBitmapSource *)cropper.Get()).AsPixelSource(ctx, nameof(IWICBitmapClipper)));
            ctx.Settings.Crop = ctx.Source.Area.ToGdiRect();
        }
示例#9
0
        public WicImageEncoderFrame(PipelineContext ctx, WicImageEncoder encoder, PixelArea area = default)
        {
            var fmt       = ctx.Settings.SaveFormat;
            var encArea   = area.IsEmpty ? ctx.Source.Area : area;
            var colorMode = ctx.Settings.ColorProfileMode;

            var bag = default(IPropertyBag2);

            encoder.WicEncoder.CreateNewFrame(out var frame, ref bag);
            encoderFrame = ComHandle.Wrap(frame);

            using (var cbag = ComHandle.Wrap(bag))
            {
                if (fmt == FileFormat.Jpeg)
                {
                    bag.Write("ImageQuality", ctx.Settings.JpegQuality / 100f);
                }

                if (fmt == FileFormat.Jpeg && ctx.Settings.JpegSubsampleMode != ChromaSubsampleMode.Default)
                {
                    bag.Write("JpegYCrCbSubsampling", (byte)ctx.Settings.JpegSubsampleMode);
                }

                if (fmt == FileFormat.Tiff)
                {
                    bag.Write("TiffCompressionMethod", (byte)WICTiffCompressionOption.WICTiffCompressionNone);
                }

                if (fmt == FileFormat.Bmp && ctx.Source.Format.AlphaRepresentation != PixelAlphaRepresentation.None)
                {
                    bag.Write("EnableV5Header32bppBGRA", true);
                }

                frame.Initialize(bag);
            }

            frame.SetSize((uint)encArea.Width, (uint)encArea.Height);
            frame.SetResolution(ctx.Settings.DpiX > 0d ? ctx.Settings.DpiX : ctx.ImageFrame.DpiX, ctx.Settings.DpiY > 0d ? ctx.Settings.DpiY : ctx.ImageFrame.DpiY);

            bool copySourceMetadata = ctx.ImageFrame is WicImageFrame srcFrame && srcFrame.WicMetadataReader is not null && ctx.Settings.MetadataNames != Enumerable.Empty <string>();
            bool writeOrientation   = ctx.Settings.OrientationMode == OrientationMode.Preserve && ctx.ImageFrame.ExifOrientation != Orientation.Normal;
            bool writeColorContext  = colorMode == ColorProfileMode.NormalizeAndEmbed || colorMode == ColorProfileMode.Preserve || (colorMode == ColorProfileMode.Normalize && ctx.DestColorProfile != ColorProfile.sRGB && ctx.DestColorProfile != ColorProfile.sGrey);

            if ((copySourceMetadata || writeOrientation) && frame.TryGetMetadataQueryWriter(out var metawriter))
            {
                using var cmeta = ComHandle.Wrap(metawriter);
                if (copySourceMetadata)
                {
                    var wicFrame = (WicImageFrame)ctx.ImageFrame;
                    foreach (string prop in ctx.Settings.MetadataNames)
                    {
                        if (wicFrame.WicMetadataReader !.TryGetMetadataByName(prop, out var pvar) && pvar.Value is not null)
                        {
                            metawriter.TrySetMetadataByName(prop, pvar);
                        }
                    }
                }

                if (writeOrientation)
                {
                    string orientationPath = ctx.Settings.SaveFormat == FileFormat.Jpeg ? Wic.Metadata.OrientationJpeg : Wic.Metadata.OrientationExif;
                    metawriter.TrySetMetadataByName(orientationPath, new PropVariant((ushort)ctx.ImageFrame.ExifOrientation));
                }
            }

            if (writeColorContext)
            {
                Debug.Assert(ctx.WicContext.DestColorContext is not null || ctx.DestColorProfile is not null);

                var cc = ctx.WicContext.DestColorContext;
                if (ctx.DestColorProfile == ColorProfile.sRGB)
                {
                    cc = WicColorProfile.SrgbCompact.Value.WicColorContext;
                }
                else if (ctx.DestColorProfile == ColorProfile.sGrey)
                {
                    cc = WicColorProfile.GreyCompact.Value.WicColorContext;
                }
                else if (ctx.DestColorProfile == ColorProfile.AdobeRgb)
                {
                    cc = WicColorProfile.AdobeRgb.Value.WicColorContext;
                }
                else if (ctx.DestColorProfile == ColorProfile.DisplayP3)
                {
                    cc = WicColorProfile.DisplayP3Compact.Value.WicColorContext;
                }

                frame.TrySetColorContexts(cc ?? ctx.WicContext.AddRef(WicColorProfile.CreateContextFromProfile(ctx.DestColorProfile !.ProfileBytes)));
            }
        }
        private static void buildPipeline(PipelineContext ctx, bool outputPlanar = true)
        {
            ctx.ImageFrame = ctx.ImageContainer.GetFrame(ctx.Settings.FrameIndex);

            bool processPlanar = false;
            var  wicFrame      = ctx.ImageFrame as WicImageFrame;

            if (wicFrame != null)
            {
                processPlanar = EnablePlanarPipeline && wicFrame.SupportsPlanarProcessing && ctx.Settings.Interpolation.WeightingFunction.Support >= 0.5;
                bool profilingPassThrough = processPlanar || (wicFrame.SupportsNativeScale && ctx.Settings.HybridScaleRatio > 1);
                ctx.Source = wicFrame.WicSource.AsPixelSource(nameof(IWICBitmapFrameDecode), !profilingPassThrough);
            }
            else if (ctx.ImageFrame is IYccImageFrame planarFrame)
            {
                processPlanar     = true;
                outputPlanar      = outputPlanar && planarFrame.IsFullRange && planarFrame.RgbYccMatrix.IsRouglyEqualTo(YccMatrix.Rec601);
                ctx.PlanarContext = new PipelineContext.PlanarPipelineContext(planarFrame.PixelSource.AsPixelSource(), planarFrame.PixelSourceCb.AsPixelSource(), planarFrame.PixelSourceCr.AsPixelSource());
                ctx.Source        = ctx.PlanarContext.SourceY;
            }

            MagicTransforms.AddColorProfileReader(ctx);

            ctx.FinalizeSettings();
            ctx.Settings.UnsharpMask       = ctx.UsedSettings.UnsharpMask;
            ctx.Settings.JpegQuality       = ctx.UsedSettings.JpegQuality;
            ctx.Settings.JpegSubsampleMode = ctx.UsedSettings.JpegSubsampleMode;

            var subsample = ctx.Settings.JpegSubsampleMode;

            if (processPlanar)
            {
                if (wicFrame != null && !ctx.Settings.AutoCrop && ctx.Settings.HybridScaleRatio == 1)
                {
                    var orCrop = PixelArea.FromGdiRect(ctx.Settings.Crop).DeOrient(ctx.Orientation, ctx.Source.Width, ctx.Source.Height);

                    if (wicFrame.ChromaSubsampling.IsSubsampledX() && ((orCrop.X & 1) != 0 || (orCrop.Width & 1) != 0))
                    {
                        processPlanar = false;
                    }
                    if (wicFrame.ChromaSubsampling.IsSubsampledY() && ((orCrop.Y & 1) != 0 || (orCrop.Height & 1) != 0))
                    {
                        processPlanar = false;
                    }
                }

                if (ctx.Settings.SaveFormat == FileFormat.Jpeg && ctx.Orientation.SwapsDimensions())
                {
                    if (subsample.IsSubsampledX() && (ctx.Settings.InnerSize.Width & 1) != 0)
                    {
                        outputPlanar = false;
                    }
                    if (subsample.IsSubsampledY() && (ctx.Settings.InnerSize.Height & 1) != 0)
                    {
                        outputPlanar = false;
                    }
                }
            }

            if (processPlanar)
            {
                bool savePlanar = outputPlanar &&
                                  ctx.Settings.SaveFormat == FileFormat.Jpeg &&
                                  ctx.Settings.OuterSize == ctx.Settings.InnerSize &&
                                  ctx.DestColorProfile == ctx.SourceColorProfile;

                if (wicFrame != null)
                {
                    WicTransforms.AddPlanarCache(ctx);
                }

                MagicTransforms.AddPlanarCropper(ctx);
                MagicTransforms.AddPlanarHybridScaler(ctx);
                MagicTransforms.AddPlanarHighQualityScaler(ctx, savePlanar ? subsample : ChromaSubsampleMode.Subsample444);
                MagicTransforms.AddUnsharpMask(ctx);

                if (savePlanar)
                {
                    MagicTransforms.AddPlanarExifFlipRotator(ctx);
                    MagicTransforms.AddPlanarExternalFormatConverter(ctx);
                }
                else
                {
                    MagicTransforms.AddPlanarConverter(ctx);
                    MagicTransforms.AddColorspaceConverter(ctx);
                    MagicTransforms.AddExifFlipRotator(ctx);
                    MagicTransforms.AddPad(ctx);
                }
            }
            else
            {
                WicTransforms.AddNativeScaler(ctx);
                MagicTransforms.AddCropper(ctx);
                MagicTransforms.AddHybridScaler(ctx);
                WicTransforms.AddPixelFormatConverter(ctx);
                MagicTransforms.AddHybridScaler(ctx);
                MagicTransforms.AddHighQualityScaler(ctx);
                MagicTransforms.AddColorspaceConverter(ctx);
                MagicTransforms.AddMatte(ctx);
                MagicTransforms.AddUnsharpMask(ctx);
                MagicTransforms.AddExifFlipRotator(ctx);
                MagicTransforms.AddPad(ctx);
            }
        }
示例#11
0
        public WicImageEncoderFrame(PipelineContext ctx, WicImageEncoder encoder, PixelArea area = default)
        {
            var fmt       = ctx.Settings.SaveFormat;
            var encArea   = area.IsEmpty ? ctx.Source.Area : area;
            var colorMode = ctx.Settings.ColorProfileMode;

            using var frame = default(ComPtr <IWICBitmapFrameEncode>);
            using (var pbag = default(ComPtr <IPropertyBag2>))
            {
                HRESULT.Check(encoder.WicEncoder->CreateNewFrame(frame.GetAddressOf(), pbag.GetAddressOf()));

                if (fmt == FileFormat.Jpeg)
                {
                    pbag.Write("ImageQuality", ctx.Settings.JpegQuality / 100f);
                }

                if (fmt == FileFormat.Jpeg && ctx.Settings.JpegSubsampleMode != ChromaSubsampleMode.Default)
                {
                    pbag.Write("JpegYCrCbSubsampling", (byte)ctx.Settings.JpegSubsampleMode);
                }

                if (fmt == FileFormat.Tiff)
                {
                    pbag.Write("TiffCompressionMethod", (byte)WICTiffCompressionOption.WICTiffCompressionNone);
                }

                if (fmt == FileFormat.Bmp && ctx.Source.Format.AlphaRepresentation != PixelAlphaRepresentation.None)
                {
                    pbag.Write("EnableV5Header32bppBGRA", true);
                }

                HRESULT.Check(frame.Get()->Initialize(pbag));
            }

            HRESULT.Check(frame.Get()->SetSize((uint)encArea.Width, (uint)encArea.Height));
            HRESULT.Check(frame.Get()->SetResolution(ctx.Settings.DpiX > 0d ? ctx.Settings.DpiX : ctx.ImageFrame.DpiX, ctx.Settings.DpiY > 0d ? ctx.Settings.DpiY : ctx.ImageFrame.DpiY));

            bool copySourceMetadata = ctx.ImageFrame is WicImageFrame srcFrame && srcFrame.WicMetadataReader is not null && ctx.Settings.MetadataNames != Enumerable.Empty <string>();
            bool writeOrientation   = ctx.Settings.OrientationMode == OrientationMode.Preserve && ctx.ImageFrame.ExifOrientation != Orientation.Normal;
            bool writeColorContext  = colorMode == ColorProfileMode.NormalizeAndEmbed || colorMode == ColorProfileMode.Preserve || (colorMode == ColorProfileMode.Normalize && ctx.DestColorProfile != ColorProfile.sRGB && ctx.DestColorProfile != ColorProfile.sGrey);

            using var metawriter = default(ComPtr <IWICMetadataQueryWriter>);
            if ((copySourceMetadata || writeOrientation) && SUCCEEDED(frame.Get()->GetMetadataQueryWriter(metawriter.GetAddressOf())))
            {
                if (copySourceMetadata)
                {
                    var wicFrame = (WicImageFrame)ctx.ImageFrame;
                    foreach (string prop in ctx.Settings.MetadataNames)
                    {
                        var pv = default(PROPVARIANT);
                        if (SUCCEEDED(wicFrame.WicMetadataReader->GetMetadataByName(prop, &pv)) && pv.vt != (ushort)VARENUM.VT_EMPTY)
                        {
                            _ = metawriter.Get()->SetMetadataByName(prop, &pv);
                        }
                    }
                }

                if (writeOrientation)
                {
                    string orientationPath = ctx.Settings.SaveFormat == FileFormat.Jpeg ? Wic.Metadata.OrientationJpeg : Wic.Metadata.OrientationExif;
                    var    pv = new PROPVARIANT {
                        vt = (ushort)VARENUM.VT_UI2
                    };
                    pv.Anonymous.uiVal = (ushort)ctx.ImageFrame.ExifOrientation;
                    _ = metawriter.Get()->SetMetadataByName(orientationPath, &pv);
                }
            }

            if (writeColorContext)
            {
                Debug.Assert(ctx.WicContext.DestColorContext is not null || ctx.DestColorProfile is not null);

                var cc = ctx.WicContext.DestColorContext;
                if (ctx.DestColorProfile == ColorProfile.sRGB)
                {
                    cc = WicColorProfile.SrgbCompact.Value.WicColorContext;
                }
                else if (ctx.DestColorProfile == ColorProfile.sGrey)
                {
                    cc = WicColorProfile.GreyCompact.Value.WicColorContext;
                }
                else if (ctx.DestColorProfile == ColorProfile.AdobeRgb)
                {
                    cc = WicColorProfile.AdobeRgb.Value.WicColorContext;
                }
                else if (ctx.DestColorProfile == ColorProfile.DisplayP3)
                {
                    cc = WicColorProfile.DisplayP3Compact.Value.WicColorContext;
                }

                using var ccc = default(ComPtr <IWICColorContext>);
                if (cc is null)
                {
                    ccc.Attach(WicColorProfile.CreateContextFromProfile(ctx.DestColorProfile !.ProfileBytes));
                    cc = ccc;
                }

                _ = frame.Get()->SetColorContexts(1, &cc);
            }

            WicEncoderFrame = frame.Detach();
        }
示例#12
0
        public WicImageEncoderFrame(PipelineContext ctx, WicImageEncoder encoder, PixelArea area = default)
        {
            var fmt       = ctx.Settings.SaveFormat;
            var encArea   = area.IsEmpty ? ctx.Source.Area : area;
            var colorMode = ctx.Settings.ColorProfileMode;

            using var frame = default(ComPtr <IWICBitmapFrameEncode>);
            using (var pbag = default(ComPtr <IPropertyBag2>))
            {
                HRESULT.Check(encoder.WicEncoder->CreateNewFrame(frame.GetAddressOf(), pbag.GetAddressOf()));

                if (fmt == FileFormat.Jpeg)
                {
                    pbag.Write("ImageQuality", ctx.Settings.JpegQuality / 100f);
                }

                if (fmt == FileFormat.Jpeg && ctx.Settings.JpegSubsampleMode != ChromaSubsampleMode.Default)
                {
                    pbag.Write("JpegYCrCbSubsampling", (byte)ctx.Settings.JpegSubsampleMode);
                }

                if (fmt == FileFormat.Tiff)
                {
                    pbag.Write("TiffCompressionMethod", (byte)WICTiffCompressionOption.WICTiffCompressionNone);
                }

                if (fmt == FileFormat.Bmp && ctx.Source.Format.AlphaRepresentation != PixelAlphaRepresentation.None)
                {
                    pbag.Write("EnableV5Header32bppBGRA", true);
                }

                HRESULT.Check(frame.Get()->Initialize(pbag));
            }

            HRESULT.Check(frame.Get()->SetSize((uint)encArea.Width, (uint)encArea.Height));
            HRESULT.Check(frame.Get()->SetResolution(ctx.Settings.DpiX > 0d ? ctx.Settings.DpiX : ctx.ImageFrame.DpiX, ctx.Settings.DpiY > 0d ? ctx.Settings.DpiY : ctx.ImageFrame.DpiY));

            bool copySourceMetadata = ctx.ImageFrame is WicImageFrame srcFrame && srcFrame.WicMetadataReader is not null && ctx.Settings.MetadataNames != Enumerable.Empty <string>();
            bool writeOrientation   = ctx.Settings.OrientationMode == OrientationMode.Preserve && ctx.ImageFrame.ExifOrientation != Orientation.Normal;
            bool writeColorContext  = colorMode == ColorProfileMode.NormalizeAndEmbed || colorMode == ColorProfileMode.Preserve || (colorMode == ColorProfileMode.Normalize && ctx.DestColorProfile != ColorProfile.sRGB && ctx.DestColorProfile != ColorProfile.sGrey);

            using var metawriter = default(ComPtr <IWICMetadataQueryWriter>);
            if ((copySourceMetadata || writeOrientation) && SUCCEEDED(frame.Get()->GetMetadataQueryWriter(metawriter.GetAddressOf())))
            {
                if (copySourceMetadata)
                {
                    var wicFrame = (WicImageFrame)ctx.ImageFrame;
                    foreach (string prop in ctx.Settings.MetadataNames)
                    {
                        var pv = default(PROPVARIANT);
                        if (SUCCEEDED(wicFrame.WicMetadataReader->GetMetadataByName(prop, &pv)) && pv.vt != (ushort)VARENUM.VT_EMPTY)
                        {
                            _ = metawriter.Get()->SetMetadataByName(prop, &pv);
                        }
                    }
                }

                if (writeOrientation)
                {
                    string orientationPath = ctx.Settings.SaveFormat == FileFormat.Jpeg ? Wic.Metadata.OrientationJpeg : Wic.Metadata.OrientationExif;
                    var    pv = new PROPVARIANT {
                        vt = (ushort)VARENUM.VT_UI2
                    };
                    pv.Anonymous.uiVal = (ushort)ctx.ImageFrame.ExifOrientation;
                    _ = metawriter.Get()->SetMetadataByName(orientationPath, &pv);
                }
            }

            if (writeColorContext)
            {
                Debug.Assert(ctx.WicContext.DestColorContext is not null || ctx.DestColorProfile is not null);

                var cc = ctx.WicContext.DestColorContext;
                if (ctx.DestColorProfile == ColorProfile.sRGB)
                {
                    cc = WicColorProfile.SrgbCompact.Value.WicColorContext;
                }
                else if (ctx.DestColorProfile == ColorProfile.sGrey)
                {
                    cc = WicColorProfile.GreyCompact.Value.WicColorContext;
                }
                else if (ctx.DestColorProfile == ColorProfile.AdobeRgb)
                {
                    cc = WicColorProfile.AdobeRgb.Value.WicColorContext;
                }
                else if (ctx.DestColorProfile == ColorProfile.DisplayP3)
                {
                    cc = WicColorProfile.DisplayP3Compact.Value.WicColorContext;
                }

                // WIC writes gAMA and cHRM tags along with iCCP when SetColorContexts is called on a PNG frame.
                // Chromium ignores the iCCP tag if the others are present, so we try to write it alone explicitly.
                if (fmt == FileFormat.Png && ctx.DestColorProfile is not null && metawriter.Get() is not null)
                {
                    var name = (ReadOnlySpan <byte>) new[] { (byte)'I', (byte)'C', (byte)'C', (byte)'\0' };
                    fixed(byte *pprofile = ctx.DestColorProfile.ProfileBytes)
                    {
                        var pvn = new PROPVARIANT {
                            vt = (ushort)VARENUM.VT_LPSTR
                        };

                        pvn.Anonymous.pszVal = (sbyte *)Unsafe.AsPointer(ref MemoryMarshal.GetReference(name));
                        _ = metawriter.Get()->SetMetadataByName(Wic.Metadata.Png.IccProfileName, &pvn);

                        var pvv = new PROPVARIANT {
                            vt = (ushort)(VARENUM.VT_UI1 | VARENUM.VT_VECTOR)
                        };

                        pvv.Anonymous.blob.pBlobData = pprofile;
                        pvv.Anonymous.blob.cbSize    = (uint)ctx.DestColorProfile.ProfileBytes.Length;
                        _ = metawriter.Get()->SetMetadataByName(Wic.Metadata.Png.IccProfileData, &pvv);
                    }
                }
                else
                {
                    using var ccc = default(ComPtr <IWICColorContext>);
                    if (cc is null)
                    {
                        ccc.Attach(WicColorProfile.CreateContextFromProfile(ctx.DestColorProfile !.ProfileBytes));
                        cc = ccc;
                    }

                    _ = frame.Get()->SetColorContexts(1, &cc);
                }
            }