Пример #1
0
        public static void ReplayGifAnimationContext(WicGifContainer cont, int playTo)
        {
            var anictx = cont.AnimationContext ??= new GifAnimationContext();

            if (anictx.LastFrame > playTo)
            {
                anictx.LastFrame = -1;
            }

            for (; anictx.LastFrame < playTo; anictx.LastFrame++)
            {
                using var frame = ComHandle.Wrap(cont.WicDecoder.GetFrame((uint)(anictx.LastFrame + 1)));
                using var meta  = ComHandle.Wrap(frame.ComObject.GetMetadataQueryReader());

                var disp = ((GifDisposalMethod)meta.ComObject.GetValueOrDefault <byte>(Wic.Metadata.Gif.FrameDisposal)).Clamp();
                if (disp == GifDisposalMethod.Preserve)
                {
                    using var conv = ComHandle.Wrap(Wic.Factory.CreateFormatConverter());
                    conv.ComObject.Initialize(frame.ComObject, Consts.GUID_WICPixelFormat32bppBGRA, WICBitmapDitherType.WICBitmapDitherTypeNone, null, 0.0, WICBitmapPaletteType.WICBitmapPaletteTypeCustom);

                    UpdateGifAnimationContext(cont, conv.ComObject, meta.ComObject);
                }

                anictx.LastDisposal = disp;
            }
        }
Пример #2
0
        private void writeFrame(BufferFrame src)
        {
            var  curFormat = ctx.Source.Format;
            var  newFormat = PixelFormat.Indexed8Bpp;
            bool alpha     = src.Trans || src.Disposal != GifDisposalMethod.RestoreBackground;

            using var pal = ComHandle.Wrap(Wic.Factory.CreatePalette());
            pal.ComObject.InitializeFromBitmap(wicSource, 256u, alpha);
            ctx.WicContext.DestPalette = pal.ComObject;

            using var conv = ComHandle.Wrap(Wic.Factory.CreateFormatConverter());
            conv.ComObject.Initialize(wicSource, newFormat.FormatGuid, WICBitmapDitherType.WICBitmapDitherTypeErrorDiffusion, pal.ComObject, 25.0, WICBitmapPaletteType.WICBitmapPaletteTypeCustom);
            ctx.Source = conv.ComObject.AsPixelSource($"{nameof(IWICFormatConverter)}: {curFormat.Name}->{newFormat.Name}", false);

            using var frm = new WicImageEncoderFrame(ctx, encoder, src.Area);

            using var frmmeta = ComHandle.Wrap(frm.WicEncoderFrame.GetMetadataQueryWriter());
            var fm = frmmeta.ComObject;

            fm.SetMetadataByName(Wic.Metadata.Gif.FrameDisposal, new PropVariant((byte)src.Disposal));
            fm.SetMetadataByName(Wic.Metadata.Gif.FrameDelay, new PropVariant((ushort)src.Delay));

            if (src.Area.X != 0)
            {
                fm.SetMetadataByName(Wic.Metadata.Gif.FrameLeft, new PropVariant((ushort)src.Area.X));
            }
            if (src.Area.Y != 0)
            {
                fm.SetMetadataByName(Wic.Metadata.Gif.FrameTop, new PropVariant((ushort)src.Area.Y));
            }

            if (alpha)
            {
                fm.SetMetadataByName(Wic.Metadata.Gif.TransparencyFlag, new PropVariant(true));
                fm.SetMetadataByName(Wic.Metadata.Gif.TransparentColorIndex, new PropVariant((byte)(pal.ComObject.GetColorCount() - 1)));
            }

            frm.WriteSource(ctx, src.Area);
        }
Пример #3
0
        public WicGifContainer(IWICBitmapDecoder dec, WicPipelineContext ctx) : base(dec, ctx, FileFormat.Gif)
        {
            using var wicmeta = ComHandle.Wrap(dec.GetMetadataQueryReader());
            var meta = wicmeta.ComObject;

            ScreenWidth  = meta.GetValueOrDefault <ushort>(Wic.Metadata.Gif.LogicalScreenWidth);
            ScreenHeight = meta.GetValueOrDefault <ushort>(Wic.Metadata.Gif.LogicalScreenHeight);

            if (meta.GetValueOrDefault <bool>(Wic.Metadata.Gif.GlobalPaletteFlag))
            {
                using var wicpal = ComHandle.Wrap(Wic.Factory.CreatePalette());
                var pal = wicpal.ComObject;
                dec.CopyPalette(pal);

                uint pcc = pal.GetColorCount();
                uint idx = meta.GetValueOrDefault <byte>(Wic.Metadata.Gif.BackgroundColorIndex);
                if (idx < pcc)
                {
                    var buff = ArrayPool <uint> .Shared.Rent((int)pcc);

                    pal.GetColors(pcc, buff);
                    BackgroundColor = buff[idx];

                    ArrayPool <uint> .Shared.Return(buff);
                }
            }

            var appext = meta.GetValueOrDefault <byte[]>(Wic.Metadata.Gif.AppExtension).AsSpan();

            if (appext.Length == 11 && netscape2_0.SequenceEqual(appext) || animexts1_0.SequenceEqual(appext))
            {
                var appdata = meta.GetValueOrDefault <byte[]>(Wic.Metadata.Gif.AppExtensionData).AsSpan();
                if (appdata.Length >= 4 && appdata[0] >= 3 && appdata[1] == 1)
                {
                    LoopCount = BinaryPrimitives.ReadUInt16LittleEndian(appdata.Slice(2));
                }
            }
        }
Пример #4
0
        public void WriteGlobalMetadata()
        {
            var cnt = ctx.ImageContainer as WicGifContainer ?? throw new InvalidOperationException("Source must be a GIF");

            using var decmeta = ComHandle.Wrap(cnt.WicDecoder.GetMetadataQueryReader());
            using var encmeta = ComHandle.Wrap(encoder.WicEncoder.GetMetadataQueryWriter());
            var dm = decmeta.ComObject;
            var em = encmeta.ComObject;

            if (dm.TryGetMetadataByName(Wic.Metadata.Gif.AppExtension, out var appext))
            {
                em.SetMetadataByName(Wic.Metadata.Gif.AppExtension, appext);
            }
            if (dm.TryGetMetadataByName(Wic.Metadata.Gif.AppExtensionData, out var appdata))
            {
                em.SetMetadataByName(Wic.Metadata.Gif.AppExtensionData, appdata);
            }
            if (dm.TryGetMetadataByName(Wic.Metadata.Gif.PixelAspectRatio, out var aspect))
            {
                em.SetMetadataByName(Wic.Metadata.Gif.PixelAspectRatio, aspect);
            }

            // TODO WIC ignores these and sets the logical screen descriptor dimensions from the first frame
            //em.SetMetadataByName(Wic.Metadata.Gif.LogicalScreenWidth, new PropVariant((ushort)ctx.Source.Width));
            //em.SetMetadataByName(Wic.Metadata.Gif.LogicalScreenHeight, new PropVariant((ushort)ctx.Source.Height));

            if (dm.GetValueOrDefault <bool>(Wic.Metadata.Gif.GlobalPaletteFlag))
            {
                using var pal = ComHandle.Wrap(Wic.Factory.CreatePalette());
                cnt.WicDecoder.CopyPalette(pal.ComObject);
                encoder.WicEncoder.SetPalette(pal.ComObject);

                if (dm.TryGetMetadataByName(Wic.Metadata.Gif.PixelAspectRatio, out var bgidx))
                {
                    em.SetMetadataByName(Wic.Metadata.Gif.BackgroundColorIndex, bgidx);
                }
            }
        }
Пример #5
0
 public WicImageEncoder(FileFormat format, IStream stm)
 {
     encoder = ComHandle.Wrap(Wic.Factory.CreateEncoder(formatMap.GetValueOrDefault(format, Consts.GUID_ContainerFormatPng), null));
     encoder.ComObject.Initialize(stm, WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache);
 }
Пример #6
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)));
            }
        }
Пример #7
0
        unsafe public void WriteAnimatedGif(PipelineContext ctx)
        {
            var cnt = ctx.ImageContainer as WicGifContainer ?? throw new NotSupportedException("Source must be a GIF");

            using (var decmeta = ComHandle.Wrap(cnt.WicDecoder.GetMetadataQueryReader()))
                using (var encmeta = ComHandle.Wrap(WicEncoder.GetMetadataQueryWriter()))
                {
                    if (decmeta.ComObject.TryGetMetadataByName(Wic.Metadata.Gif.AppExtension, out var appext))
                    {
                        encmeta.ComObject.SetMetadataByName(Wic.Metadata.Gif.AppExtension, appext);
                    }
                    if (decmeta.ComObject.TryGetMetadataByName(Wic.Metadata.Gif.AppExtensionData, out var appdata))
                    {
                        encmeta.ComObject.SetMetadataByName(Wic.Metadata.Gif.AppExtensionData, appdata);
                    }
                    if (decmeta.ComObject.TryGetMetadataByName(Wic.Metadata.Gif.PixelAspectRatio, out var aspect))
                    {
                        encmeta.ComObject.SetMetadataByName(Wic.Metadata.Gif.PixelAspectRatio, aspect);
                    }
                }

            using var buffer = new FrameBufferSource(ctx.Source.Width, ctx.Source.Height, ctx.Source.Format);
            var bspan      = buffer.Span;
            var lastSource = ctx.Source;
            var wicBuffer  = buffer.AsIWICBitmapSource();

            var anictx = cnt.AnimationContext ??= new GifAnimationContext();

            for (int i = 0; i < ctx.ImageContainer.FrameCount; i++)
            {
                if (i > 0)
                {
                    ctx.Settings.FrameIndex = i;

                    ctx.ImageFrame.Dispose();
                    ctx.ImageFrame = ctx.ImageContainer.GetFrame(ctx.Settings.FrameIndex);

                    if (ctx.ImageFrame is WicImageFrame wicFrame)
                    {
                        ctx.Source = wicFrame.WicSource.AsPixelSource(nameof(IWICBitmapFrameDecode), true);
                    }
                    else
                    {
                        ctx.Source = ctx.ImageFrame.PixelSource.AsPixelSource();
                    }

                    MagicTransforms.AddGifFrameBuffer(ctx, false);

                    if (lastSource is ChainedPixelSource chain)
                    {
                        chain.ReInit(ctx.Source);
                        ctx.Source = chain;
                    }
                }

                fixed(byte *pbuff = bspan)
                {
                    ctx.Source.CopyPixels(ctx.Source.Area, buffer.Stride, bspan.Length, (IntPtr)pbuff);

                    var  curFormat = ctx.Source.Format;
                    var  newFormat = PixelFormat.Indexed8Bpp;
                    bool alpha     = curFormat.AlphaRepresentation != PixelAlphaRepresentation.None;

                    using var pal = ComHandle.Wrap(Wic.Factory.CreatePalette());
                    pal.ComObject.InitializeFromBitmap(wicBuffer, 256u, alpha);
                    ctx.WicContext.DestPalette = pal.ComObject;

                    using var conv = ComHandle.Wrap(Wic.Factory.CreateFormatConverter());
                    conv.ComObject.Initialize(wicBuffer, newFormat.FormatGuid, WICBitmapDitherType.WICBitmapDitherTypeErrorDiffusion, pal.ComObject, 10.0, WICBitmapPaletteType.WICBitmapPaletteTypeCustom);
                    ctx.Source = conv.ComObject.AsPixelSource($"{nameof(IWICFormatConverter)}: {curFormat.Name}->{newFormat.Name}", false);

                    using var frm = new WicImageEncoderFrame(ctx, this);

                    var srcmeta = ((WicImageFrame)ctx.ImageFrame).WicMetadataReader !;

                    using (var frmmeta = ComHandle.Wrap(frm.WicEncoderFrame.GetMetadataQueryWriter()))
                    {
                        var disp = srcmeta.TryGetMetadataByName(Wic.Metadata.Gif.FrameDisposal, out var fdisp) && (byte)fdisp.Value ! == (byte)GifDisposalMethod.RestoreBackground ? GifDisposalMethod.RestoreBackground : GifDisposalMethod.Undefined;

                        frmmeta.ComObject.SetMetadataByName(Wic.Metadata.Gif.FrameDisposal, new PropVariant((byte)disp));
                        if (srcmeta.TryGetMetadataByName(Wic.Metadata.Gif.FrameDelay, out var delay))
                        {
                            frmmeta.ComObject.SetMetadataByName(Wic.Metadata.Gif.FrameDelay, delay);
                        }
                        if (alpha)
                        {
                            frmmeta.ComObject.SetMetadataByName(Wic.Metadata.Gif.TransparencyFlag, new PropVariant(true));
                            frmmeta.ComObject.SetMetadataByName(Wic.Metadata.Gif.TransparentColorIndex, new PropVariant((byte)(pal.ComObject.GetColorCount() - 1)));
                        }
                    }

                    frm.WriteSource(ctx);
                }
            }
        }
Пример #8
0
        public WicImageFrame(WicImageContainer decoder, uint index)
        {
            WicFrame  = comHandles.AddRef(decoder.WicDecoder.GetFrame(index));
            WicSource = WicFrame;
            Container = decoder;

            WicFrame.GetResolution(out double dpix, out double dpiy);
            DpiX = dpix;
            DpiY = dpiy;

            WicFrame.GetSize(out uint frameWidth, out uint frameHeight);

            if (WicFrame.TryGetMetadataQueryReader(out var metareader))
            {
                WicMetadataReader = comHandles.AddRef(metareader);

                string orientationPath =
                    MagicImageProcessor.EnableXmpOrientation ? Wic.Metadata.OrientationWindowsPolicy :
                    Container.ContainerFormat == FileFormat.Jpeg ? Wic.Metadata.OrientationJpeg :
                    Wic.Metadata.OrientationExif;

                ExifOrientation = ((Orientation)metareader.GetValueOrDefault <ushort>(orientationPath)).Clamp();
            }

            if (decoder.IsRawContainer && index == 0 && decoder.WicDecoder.TryGetPreview(out var preview))
            {
                using var pvwSource = ComHandle.Wrap(preview);
                preview.GetSize(out uint pw, out uint ph);

                if (pw == frameWidth && ph == frameHeight)
                {
                    WicSource = comHandles.AddOwnRef(preview);
                }
            }

            if (WicSource is IWICBitmapSourceTransform trans)
            {
                uint pw = 1, ph = 1;
                trans.GetClosestSize(ref pw, ref ph);

                SupportsNativeScale = pw < frameWidth || ph < frameHeight;
            }

            if (WicSource is IWICPlanarBitmapSourceTransform ptrans)
            {
                var desc = ArrayPool <WICBitmapPlaneDescription> .Shared.Rent(WicTransforms.PlanarPixelFormats.Length);

                uint twidth = frameWidth, theight = frameHeight;
                SupportsPlanarProcessing = ptrans.DoesSupportTransform(
                    ref twidth, ref theight,
                    WICBitmapTransformOptions.WICBitmapTransformRotate0, WICPlanarOptions.WICPlanarOptionsDefault,
                    WicTransforms.PlanarPixelFormats, desc, (uint)WicTransforms.PlanarPixelFormats.Length
                    );

                ChromaSubsampling =
                    desc[1].Width < desc[0].Width && desc[1].Height < desc[0].Height ? WICJpegYCrCbSubsamplingOption.WICJpegYCrCbSubsampling420 :
                    desc[1].Width < desc[0].Width ? WICJpegYCrCbSubsamplingOption.WICJpegYCrCbSubsampling422 :
                    desc[1].Height < desc[0].Height ? WICJpegYCrCbSubsamplingOption.WICJpegYCrCbSubsampling440 :
                    WICJpegYCrCbSubsamplingOption.WICJpegYCrCbSubsampling444;

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

            if (PixelFormat.FromGuid(WicSource.GetPixelFormat()).NumericRepresentation == PixelNumericRepresentation.Indexed)
            {
                var newFormat = PixelFormat.Bgr24Bpp;
                if (Container.ContainerFormat == FileFormat.Gif && Container.FrameCount > 1)
                {
                    newFormat = PixelFormat.Bgra32Bpp;
                }
                else
                {
                    using var wicpal = ComHandle.Wrap(Wic.Factory.CreatePalette());
                    var pal = wicpal.ComObject;
                    WicSource.CopyPalette(pal);

                    if (pal.HasAlpha())
                    {
                        newFormat = PixelFormat.Bgra32Bpp;
                    }
                    else if (pal.IsGrayscale() || pal.IsBlackWhite())
                    {
                        newFormat = PixelFormat.Grey8Bpp;
                    }
                }

                var conv = comHandles.AddRef(Wic.Factory.CreateFormatConverter());
                conv.Initialize(WicSource, newFormat.FormatGuid, WICBitmapDitherType.WICBitmapDitherTypeNone, null, 0.0, WICBitmapPaletteType.WICBitmapPaletteTypeCustom);
                WicSource = conv;
            }
        }