@@ -1925,35 +1925,182 @@ extension JPEG.Data.Spectral
19251925
19261926 let frame : JPEG . Header . Frame = self . encode ( )
19271927 try stream. format ( marker: . frame( frame. process) , tail: frame. serialized ( ) )
1928- for (qi , scans ) : ( [ JPEG . Table . Quantization . Key ] , [ JPEG . Scan ] ) in
1929- self . layout . definitions
1928+
1929+ switch frame . process
19301930 {
1931- let quanta : [ JPEG . Table . Quantization ] = qi. map
1931+ case . progressive:
1932+ // Progressive JPEGs may redefine tables between scans (e.g.
1933+ // between successive-approximation passes). Keep the existing
1934+ // interleaved table/scan emission order.
1935+ for (qi, scans) : ( [ JPEG . Table . Quantization . Key ] , [ JPEG . Scan ] ) in
1936+ self . layout. definitions
19321937 {
1933- self . quanta [ self . quanta. index ( forKey: $0) ]
1938+ let quanta : [ JPEG . Table . Quantization ] = qi. map
1939+ {
1940+ self . quanta [ self . quanta. index ( forKey: $0) ]
1941+ }
1942+
1943+ if !quanta. isEmpty
1944+ {
1945+ try stream. format ( marker: . quantization,
1946+ tail: JPEG . Table. serialize ( quanta) )
1947+ }
1948+
1949+ for scan : JPEG . Scan in scans
1950+ {
1951+ let dc : [ JPEG . Table . HuffmanDC ] ,
1952+ ac : [ JPEG . Table . HuffmanAC ] ,
1953+ header : JPEG . Header . Scan ,
1954+ ecs : [ UInt8 ]
1955+
1956+ ( dc, ac, header, ecs) = self . encode ( scan: scan)
1957+
1958+ if !dc. isEmpty || !ac. isEmpty
1959+ {
1960+ try stream. format ( marker: . huffman,
1961+ tail: JPEG . Table. serialize ( dc, ac) )
1962+ }
1963+
1964+ try stream. format ( marker: . scan, tail: header. serialized ( ) )
1965+ try stream. format ( prefix: ecs)
1966+ }
19341967 }
19351968
1936- if !quanta. isEmpty
1969+ default :
1970+ // For baseline, extended, and lossless processes, emit all table
1971+ // definitions (DQT, DHT) before the first SOS marker.
1972+ //
1973+ // While ITU-T T.81 technically permits inter-scan table
1974+ // definitions for all processes, many real-world decoders —
1975+ // notably Apple's ImageIO (CGImageSourceCreateImageAtIndex) —
1976+ // reject baseline JPEGs that contain DQT or DHT markers between
1977+ // SOS markers, returning error -59.
1978+
1979+ // Pre-encode every scan, preserving definition-group association
1980+ // so we can fall back to interleaved emission if needed.
1981+ var encodedGroups :
1982+ [ (
1983+ quanta: [ JPEG . Table . Quantization ] ,
1984+ scans:
1985+ [ (
1986+ dc: [ JPEG . Table . HuffmanDC ] ,
1987+ ac: [ JPEG . Table . HuffmanAC ] ,
1988+ header: JPEG . Header . Scan ,
1989+ ecs: [ UInt8 ]
1990+ ) ]
1991+ ) ] = [ ]
1992+ for (qi, scans) : ( [ JPEG . Table . Quantization . Key ] , [ JPEG . Scan ] ) in
1993+ self . layout. definitions
19371994 {
1938- try stream. format ( marker: . quantization, tail: JPEG . Table. serialize ( quanta) )
1995+ let quanta : [ JPEG . Table . Quantization ] = qi. map
1996+ {
1997+ self . quanta [ self . quanta. index ( forKey: $0) ]
1998+ }
1999+ var encoded :
2000+ [ (
2001+ dc: [ JPEG . Table . HuffmanDC ] ,
2002+ ac: [ JPEG . Table . HuffmanAC ] ,
2003+ header: JPEG . Header . Scan ,
2004+ ecs: [ UInt8 ]
2005+ ) ] = [ ]
2006+ for scan : JPEG . Scan in scans
2007+ {
2008+ encoded. append ( self . encode ( scan: scan) )
2009+ }
2010+ encodedGroups. append ( ( quanta: quanta, scans: encoded) )
19392011 }
19402012
1941- for scan : JPEG . Scan in scans
2013+ // Check whether any Huffman table selector is reused across
2014+ // scans. When selectors collide, we cannot pre-emit all
2015+ // Huffman tables because later tables would overwrite earlier
2016+ // ones for the same slot, corrupting earlier scans.
2017+ var seenDC : Set < UInt8 > = [ ] ,
2018+ seenAC : Set < UInt8 > = [ ]
2019+ var selectorsCollide : Bool = false
2020+ for (_, scans) in encodedGroups
19422021 {
1943- let dc : [ JPEG . Table . HuffmanDC ] ,
1944- ac : [ JPEG . Table . HuffmanAC ] ,
1945- header : JPEG . Header . Scan ,
1946- ecs : [ UInt8 ]
2022+ for (dc, ac, _, _) in scans
2023+ {
2024+ for table : JPEG . Table . HuffmanDC in dc
2025+ where !seenDC. insert (
2026+ JPEG . Table. HuffmanDC. serialize (
2027+ selector: table. target) ) . inserted
2028+ {
2029+ selectorsCollide = true
2030+ }
2031+ for table : JPEG . Table . HuffmanAC in ac
2032+ where !seenAC. insert (
2033+ JPEG . Table. HuffmanAC. serialize (
2034+ selector: table. target) ) . inserted
2035+ {
2036+ selectorsCollide = true
2037+ }
2038+ }
2039+ }
2040+
2041+ if selectorsCollide
2042+ {
2043+ // Selectors are reused across scans — fall back to
2044+ // per-scan table emission, matching the progressive path.
2045+ // This preserves correctness for non-Apple decoders.
2046+ // (Apple ImageIO will still reject inter-scan table
2047+ // markers for baseline, but that is inherent to the
2048+ // selector-reuse configuration and cannot be fixed
2049+ // without building cross-scan merged Huffman tables.)
2050+ for (quanta, scans) in encodedGroups
2051+ {
2052+ if !quanta. isEmpty
2053+ {
2054+ try stream. format ( marker: . quantization,
2055+ tail: JPEG . Table. serialize ( quanta) )
2056+ }
2057+ for (dc, ac, header, ecs) in scans
2058+ {
2059+ if !dc. isEmpty || !ac. isEmpty
2060+ {
2061+ try stream. format ( marker: . huffman,
2062+ tail: JPEG . Table. serialize ( dc, ac) )
2063+ }
2064+ try stream. format ( marker: . scan,
2065+ tail: header. serialized ( ) )
2066+ try stream. format ( prefix: ecs)
2067+ }
2068+ }
2069+ }
2070+ else
2071+ {
2072+ // No selector collision — safe to pre-emit all tables.
19472073
1948- ( dc, ac, header, ecs) = self . encode ( scan: scan)
2074+ // 1. Emit all quantization tables.
2075+ for (quanta, _) in encodedGroups where !quanta. isEmpty
2076+ {
2077+ try stream. format ( marker: . quantization,
2078+ tail: JPEG . Table. serialize ( quanta) )
2079+ }
19492080
1950- if !dc. isEmpty || !ac. isEmpty
2081+ // 2. Emit all Huffman tables.
2082+ for (_, scans) in encodedGroups
19512083 {
1952- try stream. format ( marker: . huffman, tail: JPEG . Table. serialize ( dc, ac) )
2084+ for (dc, ac, _, _) in scans
2085+ {
2086+ if !dc. isEmpty || !ac. isEmpty
2087+ {
2088+ try stream. format ( marker: . huffman,
2089+ tail: JPEG . Table. serialize ( dc, ac) )
2090+ }
2091+ }
19532092 }
19542093
1955- try stream. format ( marker: . scan, tail: header. serialized ( ) )
1956- try stream. format ( prefix: ecs)
2094+ // 3. Emit all scan headers and entropy-coded segments.
2095+ for (_, scans) in encodedGroups
2096+ {
2097+ for (_, _, header, ecs) in scans
2098+ {
2099+ try stream. format ( marker: . scan,
2100+ tail: header. serialized ( ) )
2101+ try stream. format ( prefix: ecs)
2102+ }
2103+ }
19572104 }
19582105 }
19592106
0 commit comments