9
9
"image"
10
10
"image/png"
11
11
"io"
12
+ "strconv"
13
+ "sync"
12
14
)
13
15
14
16
type idat []byte
@@ -38,13 +40,17 @@ func writeUint32(b []uint8, u uint32) {
38
40
39
41
// APNG encapsulates animated PNG frames, their delays, disposal methods, loop count, and global configuration.
40
42
type APNG struct {
41
- Images []image.Image // The successive images.
42
- Delays []uint16 // The successive delay times, one per frame, in 100ths of a second.
43
- Disposals []byte // The successive disposal methods, one per frame.
44
- LoopCount uint32 // The loop count. 0 indicates infinite looping.
43
+ Images []image.Image // The successive images.
44
+
45
+ // The successive delay times, one per frame, in 100ths of a second (centiseconds).
46
+ //
47
+ // Note: For 30 FPS, each frame lasts 1/30 second ≈ 3.33 centiseconds.
48
+ // When using integer delays, you might use 3 centiseconds per frame.
49
+ Delays []uint16
50
+ Disposals []byte // The successive disposal methods, one per frame.
51
+ LoopCount uint32 // The loop count. 0 indicates infinite looping.
45
52
Config image.Config
46
53
}
47
-
48
54
type encoder struct {
49
55
aPNG * APNG
50
56
writer io.Writer
@@ -281,20 +287,83 @@ func EncodeAll(w io.Writer, a *APNG) error {
281
287
282
288
_ , e .err = io .WriteString (w , pngHeader )
283
289
290
+ // Data to be used while processing the first image
291
+ var mutex sync.Mutex
292
+
293
+ // Prepare PNG data for all frames in parallel
294
+ type frameData struct {
295
+ index int
296
+ ihdr []byte
297
+ idats []idat // Changed from [][]byte to []idat
298
+ }
299
+
300
+ frameDataChan := make (chan frameData , len (a .Images ))
301
+ var wg sync.WaitGroup
302
+
284
303
for i , img := range a .Images {
285
- bb := & bytes.Buffer {}
286
- if err := png .Encode (bb , img ); err != nil {
287
- return errors .New ("apng: png encoding error(" + err .Error () + ")" )
288
- }
304
+ wg .Add (1 )
305
+ go func (index int , image image.Image ) {
306
+ defer wg .Done ()
307
+
308
+ bb := & bytes.Buffer {}
309
+ if err := png .Encode (bb , image ); err != nil {
310
+ // Use mutex to handle error state
311
+ mutex .Lock ()
312
+ if e .err == nil {
313
+ e .err = errors .New ("apng: png encoding error(" + err .Error () + ")" )
314
+ }
315
+ mutex .Unlock ()
316
+ return
317
+ }
318
+
319
+ pc , err := fetchPNGChunk (bb )
320
+ if err != nil {
321
+ mutex .Lock ()
322
+ if e .err == nil {
323
+ e .err = err
324
+ }
325
+ mutex .Unlock ()
326
+ return
327
+ }
289
328
290
- pc , err := fetchPNGChunk (bb )
291
- if err != nil {
292
- return err
329
+ // fmt.Printf("Frame %d calculated\n", index)
330
+
331
+ frameDataChan <- frameData {
332
+ index : index ,
333
+ ihdr : pc .ihdr ,
334
+ idats : pc .idats , // The type of idats returned by fetchPNGChunk should be []idat
335
+ }
336
+ }(i , img )
337
+ }
338
+
339
+ // Wait for all goroutines to complete
340
+ go func () {
341
+ wg .Wait ()
342
+ close (frameDataChan )
343
+ }()
344
+
345
+ // Write output in correct order
346
+ frameDataMap := make (map [int ]frameData )
347
+ for fd := range frameDataChan {
348
+ frameDataMap [fd .index ] = fd
349
+ }
350
+
351
+ // Error check
352
+ if e .err != nil {
353
+ return e .err
354
+ }
355
+
356
+ // Write the first frame and then other frames in correct order
357
+ for i := range a .Images {
358
+ fd , ok := frameDataMap [i ]
359
+ if ! ok {
360
+ return errors .New ("apng: missing frame data for index " + strconv .Itoa (i ))
293
361
}
294
- e .ihdr = pc .ihdr
295
- e .idats = pc .idats
296
362
297
- // First image is defalt image.
363
+ e .ihdr = fd .ihdr
364
+ e .idats = fd .idats
365
+
366
+ // The first image is the default image
298
367
if i == 0 {
299
368
e .writeIHDR ()
300
369
e .writeacTL ()
@@ -309,5 +378,4 @@ func EncodeAll(w io.Writer, a *APNG) error {
309
378
e .writeIEND ()
310
379
311
380
return e .err
312
-
313
381
}
0 commit comments