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; } }
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); }
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)); } } }
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); } } }
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); }
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))); } }
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); } } }
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; } }