Skip to content

Commit 45fc75c

Browse files
author
LoneWandererProductions
committed
sync back fixes
1 parent 6385a01 commit 45fc75c

1 file changed

Lines changed: 145 additions & 49 deletions

File tree

Imaging/ImageGifHandler.cs

Lines changed: 145 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ namespace Imaging
3434
/// </summary>
3535
public static class ImageGifHandler
3636
{
37+
/// <summary>
38+
/// Deletes the object.
39+
/// </summary>
40+
/// <param name="hObject">The h object.</param>
41+
/// <returns>Status of cleanup</returns>
42+
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
43+
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
44+
internal static extern bool DeleteObject(IntPtr hObject);
45+
3746
/// <summary>
3847
/// Gets the image information.
3948
/// </summary>
@@ -109,14 +118,15 @@ internal static async Task<List<ImageSource>> LoadGif(string path)
109118
}
110119

111120
/// <summary>
112-
/// Creates the gif.
113-
/// The gif is slightly bigger for now
114-
/// Sources:
115-
/// https://stackoverflow.com/questions/18719302/net-creating-a-looping-gif-using-gifbitmapencoder
121+
/// Creates the gif.
122+
/// The gif is slightly bigger for now
123+
/// Sources:
124+
/// https://stackoverflow.com/questions/18719302/net-creating-a-looping-gif-using-gifbitmapencoder
116125
/// </summary>
117126
/// <param name="path">The path to the folder.</param>
118127
/// <param name="target">The target path.</param>
119-
internal static void CreateGif(string path, string target)
128+
/// <param name="delayMs">Optional delay between frames in milliseconds.</param>
129+
internal static void CreateGif(string path, string target, int delayMs = 100)
120130
{
121131
//get all allowed files from target folder
122132
var lst = FileHelper.GetFilesByExtensionFullPath(path, ImagingResources.Appendix);
@@ -131,15 +141,16 @@ internal static void CreateGif(string path, string target)
131141
return;
132142
}
133143

134-
GifCreator(btm, target);
144+
GifCreator(btm, target, delayMs);
135145
}
136146

137147
/// <summary>
138-
/// Creates the GIF.
148+
/// Creates the GIF.
139149
/// </summary>
140150
/// <param name="path">The path.</param>
141151
/// <param name="target">The target.</param>
142-
internal static void CreateGif(List<string> path, string target)
152+
/// <param name="delayMs">Optional delay between frames in milliseconds.</param>
153+
internal static void CreateGif(List<string> path, string target, int delayMs = 100)
143154
{
144155
//collect and convert all images
145156
var btm = path.ConvertAll(ImageStream.GetOriginalBitmap);
@@ -149,7 +160,18 @@ internal static void CreateGif(List<string> path, string target)
149160
return;
150161
}
151162

152-
GifCreator(btm, target);
163+
GifCreator(btm, target, delayMs);
164+
}
165+
166+
/// <summary>
167+
/// Creates the GIF with variable delays per frame using Tuples.
168+
/// </summary>
169+
/// <param name="frames">Collection of Bitmap and specific DelayMs tuples.</param>
170+
/// <param name="target">The target.</param>
171+
internal static void CreateGif(IEnumerable<(Bitmap Image, int DelayMs)> frames, string target)
172+
{
173+
if (frames == null) return;
174+
GifCreator(frames, target);
153175
}
154176

155177
/// <summary>
@@ -168,40 +190,81 @@ internal static void CreateGif(IEnumerable<FrameInfo>? frames, string target)
168190
}
169191

170192
/// <summary>
171-
/// Create the gif.
193+
/// Create the gif with a fixed delay.
172194
/// </summary>
173195
/// <param name="btm">A list of Bitmaps.</param>
174196
/// <param name="target">The target.</param>
175-
private static void GifCreator(IEnumerable<Bitmap> btm, string target)
197+
/// <summary>
198+
/// Create the gif from a list of Bitmaps.
199+
/// </summary>
200+
/// <param name="btm">A list of Bitmaps.</param>
201+
/// <param name="target">The target.</param>
202+
private static void GifCreator(IEnumerable<Bitmap> btm, string target, int delayMs)
176203
{
177204
var gEnc = new GifBitmapEncoder();
205+
ushort gifDelay = (ushort)(delayMs / 10); // Convert to hundredths of a second
178206

179-
foreach (var src in btm.Select(bmpImage => bmpImage.GetHbitmap()).Select(bmp =>
180-
System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
181-
bmp,
182-
IntPtr.Zero,
183-
Int32Rect.Empty,
184-
BitmapSizeOptions.FromEmptyOptions())))
207+
foreach (var bmpImage in btm)
185208
{
186-
gEnc.Frames.Add(BitmapFrame.Create(src));
209+
IntPtr hBitmap = bmpImage.GetHbitmap();
210+
try
211+
{
212+
var src = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
213+
hBitmap,
214+
IntPtr.Zero,
215+
Int32Rect.Empty,
216+
BitmapSizeOptions.FromEmptyOptions());
217+
218+
var metadata = new BitmapMetadata("gif");
219+
metadata.SetQuery("/grctlext/Delay", gifDelay);
220+
221+
gEnc.Frames.Add(BitmapFrame.Create(src, null, metadata, null));
222+
}
223+
finally
224+
{
225+
DeleteObject(hBitmap);
226+
}
187227
}
188228

189-
using var ms = new MemoryStream();
190-
gEnc.Save(ms);
191-
var fileBytes = ms.ToArray();
192-
// write custom header
193-
// This is the NETSCAPE2.0 Application Extension.
194-
var applicationExtension =
195-
new byte[] { 33, 255, 11, 78, 69, 84, 83, 67, 65, 80, 69, 50, 46, 48, 3, 1, 0, 0, 0 };
196-
var newBytes = new List<byte>();
197-
newBytes.AddRange(fileBytes.Take(13));
198-
newBytes.AddRange(applicationExtension);
199-
newBytes.AddRange(fileBytes.Skip(13));
200-
File.WriteAllBytes(target, newBytes.ToArray());
229+
SaveWithLoopingExtension(gEnc, target);
201230
}
202231

203232
/// <summary>
204-
/// Creates the GIF.
233+
/// Create the gif with variable per-frame delay using Tuples.
234+
/// </summary>
235+
/// <param name="frames">The frames.</param>
236+
/// <param name="target">The target.</param>
237+
private static void GifCreator(IEnumerable<(Bitmap Image, int DelayMs)> frames, string target)
238+
{
239+
var gEnc = new GifBitmapEncoder();
240+
241+
foreach (var frame in frames)
242+
{
243+
IntPtr hBitmap = frame.Image.GetHbitmap();
244+
try
245+
{
246+
var src = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
247+
hBitmap,
248+
IntPtr.Zero,
249+
Int32Rect.Empty,
250+
BitmapSizeOptions.FromEmptyOptions());
251+
252+
var metadata = new BitmapMetadata("gif");
253+
metadata.SetQuery("/grctlext/Delay", (ushort)(frame.DelayMs / 10));
254+
255+
gEnc.Frames.Add(BitmapFrame.Create(src, null, metadata, null));
256+
}
257+
finally
258+
{
259+
DeleteObject(hBitmap);
260+
}
261+
}
262+
263+
SaveWithLoopingExtension(gEnc, target);
264+
}
265+
266+
/// <summary>
267+
/// Creates the GIF from FrameInfo.
205268
/// </summary>
206269
/// <param name="frames">The frames.</param>
207270
/// <param name="target">The target.</param>
@@ -213,28 +276,61 @@ private static void GifCreator(IEnumerable<FrameInfo>? frames, string target)
213276

214277
foreach (var frameInfo in frames)
215278
{
216-
var bitmapSource = BitmapFrame.Create(
217-
BitmapSource.Create(
218-
frameInfo.Image.Width,
219-
frameInfo.Image.Height,
220-
96, 96, // DPI
221-
PixelFormats.Bgra32, // Or appropriate format
222-
null, // No palette for Bgra32
223-
frameInfo.Image.LockBits(new Rectangle(0, 0, frameInfo.Image.Width, frameInfo.Image.Height),
224-
ImageLockMode.ReadOnly,
225-
PixelFormat.Format32bppArgb).Scan0,
226-
frameInfo.Image.Height * frameInfo.Image.Width * 4, // Image byte size
227-
frameInfo.Image.Width * 4)); // Bytes per row
228-
229-
var metadata = new BitmapMetadata(ImagingResources.GifMetadata);
230-
metadata.SetQuery(ImagingResources.GifMetadataQueryDelay,
231-
(ushort)(frameInfo.DelayTime * 100)); // Delay in hundredths of seconds
232-
233-
gEnc.Frames.Add(BitmapFrame.Create(bitmapSource, null, metadata, null));
279+
var img = frameInfo.Image;
280+
281+
var bmpData = img.LockBits(
282+
new Rectangle(0, 0, img.Width, img.Height),
283+
ImageLockMode.ReadOnly,
284+
PixelFormat.Format32bppArgb);
285+
286+
try
287+
{
288+
var bitmapSource = BitmapSource.Create(
289+
img.Width,
290+
img.Height,
291+
img.HorizontalResolution,
292+
img.VerticalResolution,
293+
PixelFormats.Bgra32,
294+
null,
295+
bmpData.Scan0,
296+
img.Height * bmpData.Stride,
297+
bmpData.Stride);
298+
299+
var metadata = new BitmapMetadata(ImagingResources.GifMetadata);
300+
metadata.SetQuery(ImagingResources.GifMetadataQueryDelay, (ushort)(frameInfo.DelayTime * 100));
301+
302+
gEnc.Frames.Add(BitmapFrame.Create(bitmapSource, null, metadata, null));
303+
}
304+
finally
305+
{
306+
img.UnlockBits(bmpData);
307+
}
234308
}
235309

236310
using var fs = new FileStream(target, FileMode.Create, FileAccess.Write);
237311
gEnc.Save(fs);
238312
}
313+
314+
/// <summary>
315+
/// Helper method to inject the Netscape looping extension.
316+
/// </summary>
317+
/// <param name="gEnc">The g enc.</param>
318+
/// <param name="target">The target.</param>
319+
private static void SaveWithLoopingExtension(GifBitmapEncoder gEnc, string target)
320+
{
321+
using var ms = new MemoryStream();
322+
gEnc.Save(ms);
323+
var fileBytes = ms.ToArray();
324+
325+
// This is the NETSCAPE2.0 Application Extension.
326+
var applicationExtension = new byte[] { 33, 255, 11, 78, 69, 84, 83, 67, 65, 80, 69, 50, 46, 48, 3, 1, 0, 0, 0 };
327+
328+
var newBytes = new List<byte>(fileBytes.Length + applicationExtension.Length);
329+
newBytes.AddRange(fileBytes.Take(13));
330+
newBytes.AddRange(applicationExtension);
331+
newBytes.AddRange(fileBytes.Skip(13));
332+
333+
File.WriteAllBytes(target, newBytes.ToArray());
334+
}
239335
}
240336
}

0 commit comments

Comments
 (0)