private void loadFrame(BufferFrame frame) { using var srcmeta = new ComPtr <IWICMetadataQueryReader>(((WicImageFrame)ctx.ImageFrame).WicMetadataReader); frame.Disposal = (GifDisposalMethod)srcmeta.GetValueOrDefault <byte>(Wic.Metadata.Gif.FrameDisposal) == GifDisposalMethod.RestoreBackground ? GifDisposalMethod.RestoreBackground : GifDisposalMethod.Preserve; frame.Delay = srcmeta.GetValueOrDefault <ushort>(Wic.Metadata.Gif.FrameDelay); frame.Trans = srcmeta.GetValueOrDefault <bool>(Wic.Metadata.Gif.TransparencyFlag); frame.Area = ctx.Source.Area; var buff = frame.Source; fixed(byte *pbuff = buff.Span) ctx.Source.CopyPixels(frame.Area, buff.Stride, buff.Span.Length, (IntPtr)pbuff); }
public static (int Left, int Top, int Width, int Height, bool Alpha, bool FullScreen, GifDisposalMethod Disposal) GetGifFrameInfo(WicGifContainer cont, ComPtr <IWICBitmapSource> frame, ComPtr <IWICMetadataQueryReader> meta) { uint width, height; HRESULT.Check(frame.Get()->GetSize(&width, &height)); int left = 0, top = 0; var disp = ((GifDisposalMethod)meta.GetValueOrDefault <byte>(Wic.Metadata.Gif.FrameDisposal)).Clamp(); bool trans = meta.GetValueOrDefault <bool>(Wic.Metadata.Gif.TransparencyFlag); bool full = width >= cont.ScreenWidth && height >= cont.ScreenHeight; if (!full) { left = meta.GetValueOrDefault <ushort>(Wic.Metadata.Gif.FrameLeft); top = meta.GetValueOrDefault <ushort>(Wic.Metadata.Gif.FrameTop); } width = Math.Min(width, cont.ScreenWidth - (uint)left); height = Math.Min(height, cont.ScreenHeight - (uint)top); return(left, top, (int)width, (int)height, trans, full, disp); }
public static void UpdateGifAnimationContext(WicGifContainer cont, ComPtr <IWICBitmapSource> src, ComPtr <IWICMetadataQueryReader> meta) { Debug.Assert(cont.AnimationContext is not null); var anictx = cont.AnimationContext; const int bytesPerPixel = 4; var finfo = GetGifFrameInfo(cont, src, meta); var fbuff = anictx.FrameBufferSource ??= new FrameBufferSource(cont.ScreenWidth, cont.ScreenHeight, PixelFormat.Bgra32Bpp); var bspan = fbuff.Span; fbuff.ResumeTiming(); // Most GIF viewers clear the background to transparent instead of the background color when the next frame has transparency bool ftrans = meta.GetValueOrDefault <bool>(Wic.Metadata.Gif.TransparencyFlag); if (!finfo.FullScreen && anictx.LastDisposal == GifDisposalMethod.RestoreBackground) { MemoryMarshal.Cast <byte, uint>(bspan).Fill(ftrans ? 0 : cont.BackgroundColor); } // Similarly, they overwrite a background color with transparent pixels but overlay instead when the previous frame is preserved var fspan = bspan.Slice(finfo.Top * fbuff.Stride + finfo.Left * bytesPerPixel); fixed(byte *buff = fspan) { if (!ftrans || anictx.LastDisposal == GifDisposalMethod.RestoreBackground) { var rect = new WICRect { Width = finfo.Width, Height = finfo.Height }; HRESULT.Check(src.Get()->CopyPixels(&rect, (uint)fbuff.Stride, (uint)fspan.Length, buff)); } else { using var overlay = new OverlayTransform(fbuff, src.AsPixelSource(null, nameof(IWICBitmapFrameDecode), false), finfo.Left, finfo.Top, true, true); overlay.CopyPixels(new PixelArea(finfo.Left, finfo.Top, finfo.Width, finfo.Height), fbuff.Stride, fspan.Length, (IntPtr)buff); } } fbuff.PauseTiming(); }