@@ -416,6 +416,84 @@ func TestMultiPartWriteChunkPart(t *testing.T) {
416416 t .Logf ("Multi-part file size: %d bytes" , buf .Len ())
417417}
418418
419+ // TestMultiPartReadChunkRoundtrip verifies that ReadChunk correctly handles
420+ // the 4-byte part number prefix in multipart EXR chunk headers.
421+ func TestMultiPartReadChunkRoundtrip (t * testing.T ) {
422+ h1 := NewScanlineHeader (4 , 4 )
423+ h1 .SetCompression (CompressionNone )
424+ h1 .Set (& Attribute {Name : AttrNameName , Type : AttrTypeString , Value : "part1" })
425+
426+ h2 := NewScanlineHeader (4 , 4 )
427+ h2 .SetCompression (CompressionNone )
428+ h2 .Set (& Attribute {Name : AttrNameName , Type : AttrTypeString , Value : "part2" })
429+
430+ var buf bytes.Buffer
431+ ws := & seekableWriter {Buffer : & buf }
432+
433+ w , err := NewMultiPartWriter (ws , []* Header {h1 , h2 })
434+ if err != nil {
435+ t .Fatalf ("NewMultiPartWriter() error = %v" , err )
436+ }
437+
438+ // Write chunks with distinct data per part and scanline
439+ // Each scanline: 4 pixels * 3 channels (R,G,B half) * 2 bytes = 24 bytes
440+ chunkSize := 24
441+ written := make (map [[2 ]int ][]byte ) // [part, y] -> data
442+ for part := 0 ; part < 2 ; part ++ {
443+ for y := 0 ; y < 4 ; y ++ {
444+ data := make ([]byte , chunkSize )
445+ // Fill with recognizable pattern: part in first byte, y in second
446+ for i := range data {
447+ data [i ] = byte (part * 16 + y + i )
448+ }
449+ written [[2 ]int {part , y }] = append ([]byte (nil ), data ... )
450+ if err := w .WriteChunkPart (part , int32 (y ), data ); err != nil {
451+ t .Fatalf ("WriteChunkPart(%d, %d) error = %v" , part , y , err )
452+ }
453+ }
454+ }
455+
456+ if err := w .Close (); err != nil {
457+ t .Fatalf ("Close() error = %v" , err )
458+ }
459+
460+ // Read back and verify
461+ fileData := buf .Bytes ()
462+ r := & readerAtWrapper {bytes .NewReader (fileData )}
463+
464+ f , err := OpenReader (r , int64 (len (fileData )))
465+ if err != nil {
466+ t .Fatalf ("OpenReader() error = %v" , err )
467+ }
468+
469+ if ! f .IsMultiPart () {
470+ t .Fatal ("expected multipart file" )
471+ }
472+ if f .NumParts () != 2 {
473+ t .Fatalf ("expected 2 parts, got %d" , f .NumParts ())
474+ }
475+
476+ for part := 0 ; part < 2 ; part ++ {
477+ offsets := f .Offsets (part )
478+ if len (offsets ) != 4 {
479+ t .Fatalf ("part %d: expected 4 chunks, got %d" , part , len (offsets ))
480+ }
481+ for chunkIdx := 0 ; chunkIdx < 4 ; chunkIdx ++ {
482+ y , data , err := f .ReadChunk (part , chunkIdx )
483+ if err != nil {
484+ t .Fatalf ("ReadChunk(%d, %d) error = %v" , part , chunkIdx , err )
485+ }
486+ if int (y ) != chunkIdx {
487+ t .Errorf ("ReadChunk(%d, %d) y = %d, want %d" , part , chunkIdx , y , chunkIdx )
488+ }
489+ expected := written [[2 ]int {part , chunkIdx }]
490+ if ! bytes .Equal (data , expected ) {
491+ t .Errorf ("ReadChunk(%d, %d) data mismatch: got %v, want %v" , part , chunkIdx , data [:4 ], expected [:4 ])
492+ }
493+ }
494+ }
495+ }
496+
419497func TestMultiPartWriteInvalidPart (t * testing.T ) {
420498 h := NewScanlineHeader (4 , 4 )
421499 h .SetCompression (CompressionNone )
0 commit comments