Files
pdf-odin-llm/filter_dct.odin
2026-09-25 23:32:29 +02:00

1647 lines
55 KiB
Odin

package pdf
import "core:mem"
// DCTDecode: a JPEG decoder (ISO/IEC 10918-1), sufficient for what PDF
// producers actually emit.
//
// ISO 32000-2, 7.4.8 defines DCTDecode by reference to the JPEG specification
// and then says almost nothing else, so the interesting question is which of
// JPEG's many modes turn up inside PDFs. In practice: baseline and extended
// sequential Huffman (SOF0/SOF1), progressive Huffman (SOF2 -- ordinary once a
// document is assembled out of web assets), one, three and four components,
// and the Adobe APP14 marker that tells CMYK from YCCK. Those are all
// implemented here. Arithmetic coding (SOF9-SOF11), the lossless mode (SOF3)
// and the hierarchical modes are not: no producer emits them into a PDF, and
// there is nothing sensible to substitute.
//
// The decoder is written to survive damage. A JPEG inside a PDF has usually
// been through at least one recompressor, and truncated scans, restart markers
// at the wrong interval and missing EOI are all routine. Every one of those
// yields as much of the image as could be decoded rather than an error.
// ---------------------------------------------------------------------------
// Public surface
// ---------------------------------------------------------------------------
// Jpeg_Image is one decoded JPEG. `data` belongs to the caller.
Jpeg_Image :: struct {
width, height: int,
// 1 (grey), 3 (RGB, after the YCbCr inverse transform) or 4 (CMYK, after
// the YCCK inverse transform when the Adobe marker asks for one). Two
// components is not a real JPEG colour model but does occur; it is passed
// through untransformed.
components: int,
// Bits per sample in `data`, always 8. A 12-bit JPEG -- rare, and rarer
// still in a PDF -- is decoded and scaled down so that `data` stays one
// byte per sample; `jpeg_info` reports the precision the file declares for
// a caller that wants to know.
bits: int,
// Samples, `components` per pixel, row-major, no padding between rows.
data: []byte,
// The Adobe APP14 colour transform: 0 none, 1 YCbCr, 2 YCCK; -1 when the
// marker is absent.
//
// This is reported rather than acted on beyond the inverse transform
// itself, because Adobe writes the CMYK JPEGs it embeds in PDFs with every
// sample inverted, and the inversion is expressed in the image
// dictionary's /Decode array, not in the JPEG. Inverting here as well
// would double-apply it. image.odin owns that decision.
adobe_transform: int,
}
// An image larger than anything that can be rendered is not worth decoding,
// so this is the rasteriser's own limit from document.odin.
@(private) MAX_JPEG_PIXELS :: MAX_BITMAP_PIXELS
// Bytes one decode may allocate in total, across coefficient buffers, sample
// planes and the output. Every dimension in a JPEG header is attacker
// controlled and none of them can be checked against the compressed size --
// a uniform image compresses without limit -- so a fixed cap is the only
// defence there is.
@(private) MAX_JPEG_ALLOC :: 1 << 30
// Marker segments before the decoder gives up. A real file has a few dozen;
// this only bounds a stream of well-formed but empty ones.
@(private) MAX_JPEG_SEGMENTS :: 1 << 16
// Scans decoded before the decoder stops and keeps what it has. This is a
// separate bound from MAX_JPEG_SEGMENTS because a scan is not cheap per byte:
// one EOB run skips up to 32767 blocks for 18 bits, so a few hundred bytes of
// entropy data drives the MCU loop across the whole component. Bounding only
// the segment count leaves 65536 sweeps of a frame that may hold 2^22 blocks
// -- about 2.7e11 block visits, which is minutes to an hour of CPU from a
// stream of a megabyte or two. libjpeg-turbo's D_MAX_SCANS is the same 100,
// and it costs nothing real: no producer emits more than a few dozen, and the
// worst file in this project's corpus has ten.
@(private) MAX_JPEG_SCANS :: 100
// jpeg_decode decodes a complete JPEG datastream.
//
// `img.data` is allocated with `allocator` and is the caller's to free;
// everything else the decoder needs is scratch taken from `context.allocator`
// and released before returning.
//
// A truncated or damaged scan is not an error: the image comes back with the
// blocks that were decoded and mid-grey everywhere else, which is what every
// other reader shows. An error means there was nothing to hand back at all --
// no SOI, no frame header, impossible dimensions, or a coding mode this
// decoder does not implement.
jpeg_decode :: proc(data: []byte, allocator := context.allocator) -> (img: Jpeg_Image, err: Error) {
d, alloc_err := new(Jpeg_Decoder, context.allocator)
if alloc_err != nil {
return {}, .Out_Of_Memory
}
defer free(d, context.allocator)
d.scratch = context.allocator
d.adobe_transform = -1
defer jpeg_release(d)
if perr := jpeg_run(d, data); perr != .None {
return {}, perr
}
out := jpeg_emit(d, allocator) or_return
return Jpeg_Image{
width = d.width,
height = d.height,
components = d.ncomp,
bits = 8,
data = out,
adobe_transform = d.saw_adobe ? d.adobe_transform : -1,
}, .None
}
// jpeg_info reads the frame header only. It allocates nothing and does not
// touch the entropy-coded data, so it is cheap enough to call on every image
// in a document.
//
// `bits` is the precision the file declares (8, or 12 for the rare deep
// image), not the precision of what `jpeg_decode` hands back.
jpeg_info :: proc(data: []byte) -> (width, height, components, bits: int, err: Error) {
pos, found := jpeg_find_soi(data)
if !found {
return 0, 0, 0, 0, .Bad_Jpeg_Data
}
for _ in 0 ..< MAX_JPEG_SEGMENTS {
marker, next, ok := jpeg_next_marker(data, pos)
if !ok { break }
pos = next
switch marker {
case JPEG_SOI, JPEG_TEM, JPEG_RST0, JPEG_RST0 + 1, JPEG_RST0 + 2, JPEG_RST0 + 3,
JPEG_RST0 + 4, JPEG_RST0 + 5, JPEG_RST0 + 6, JPEG_RST0 + 7:
continue
case JPEG_EOI:
return 0, 0, 0, 0, .Bad_Jpeg_Data
}
payload, after, pok := jpeg_segment(data, pos)
if !pok { break }
pos = after
switch marker {
case JPEG_SOF0, JPEG_SOF1, JPEG_SOF2, JPEG_SOF3, JPEG_SOF5, JPEG_SOF6, JPEG_SOF7,
JPEG_SOF9, JPEG_SOF10, JPEG_SOF11, JPEG_SOF13, JPEG_SOF14, JPEG_SOF15:
if len(payload) < 6 {
return 0, 0, 0, 0, .Bad_Jpeg_Data
}
bits = int(payload[0])
height = int(payload[1]) << 8 | int(payload[2])
width = int(payload[3]) << 8 | int(payload[4])
components = int(payload[5])
if height == 0 {
// A frame header may leave the height at zero and supply it in
// a DNL marker after the first scan (ISO/IEC 10918-1, B.2.2).
if dnl, dok := jpeg_find_dnl(data, pos); dok {
height = dnl
}
}
if width <= 0 || height <= 0 || components <= 0 || components > 4 {
return width, height, components, bits, .Bad_Jpeg_Data
}
return width, height, components, bits, .None
case JPEG_SOS:
// Entropy data before a frame header: nothing to report.
return 0, 0, 0, 0, .Bad_Jpeg_Data
}
}
return 0, 0, 0, 0, .Bad_Jpeg_Data
}
// ---------------------------------------------------------------------------
// Markers
// ---------------------------------------------------------------------------
@(private) JPEG_SOF0 :: 0xC0
@(private) JPEG_SOF1 :: 0xC1
@(private) JPEG_SOF2 :: 0xC2
@(private) JPEG_SOF3 :: 0xC3
@(private) JPEG_DHT :: 0xC4
@(private) JPEG_SOF5 :: 0xC5
@(private) JPEG_SOF6 :: 0xC6
@(private) JPEG_SOF7 :: 0xC7
@(private) JPEG_SOF9 :: 0xC9
@(private) JPEG_SOF10 :: 0xCA
@(private) JPEG_SOF11 :: 0xCB
@(private) JPEG_DAC :: 0xCC
@(private) JPEG_SOF13 :: 0xCD
@(private) JPEG_SOF14 :: 0xCE
@(private) JPEG_SOF15 :: 0xCF
@(private) JPEG_RST0 :: 0xD0
@(private) JPEG_SOI :: 0xD8
@(private) JPEG_EOI :: 0xD9
@(private) JPEG_SOS :: 0xDA
@(private) JPEG_DQT :: 0xDB
@(private) JPEG_DNL :: 0xDC
@(private) JPEG_DRI :: 0xDD
@(private) JPEG_APP0 :: 0xE0
@(private) JPEG_APP14 :: 0xEE
@(private) JPEG_COM :: 0xFE
@(private) JPEG_TEM :: 0x01
// jpeg_find_soi locates the start-of-image. Junk in front of it -- an HTTP
// response, an Exif wrapper that a rewriter half-removed, a stray newline --
// is common enough in embedded streams that starting at byte 0 unconditionally
// would reject working images.
@(private)
jpeg_find_soi :: proc(data: []byte) -> (pos: int, ok: bool) {
for i := 0; i + 1 < len(data); i += 1 {
if data[i] == 0xFF && data[i + 1] == JPEG_SOI {
return i + 2, true
}
}
return 0, false
}
// jpeg_next_marker scans forward to the next marker, skipping fill bytes,
// stuffed 0xFF 0x00 pairs and any non-marker bytes in between. Returning the
// marker code and the offset just past it.
@(private)
jpeg_next_marker :: proc(data: []byte, from: int) -> (marker: int, next: int, ok: bool) {
p := max(from, 0)
for p + 1 < len(data) {
if data[p] != 0xFF {
p += 1
continue
}
q := p + 1
for q < len(data) && data[q] == 0xFF {
q += 1
}
if q >= len(data) { break }
if data[q] == 0x00 {
p = q + 1
continue
}
return int(data[q]), q + 1, true
}
return 0, len(data), false
}
// jpeg_segment reads a length-prefixed marker segment starting at `pos` (which
// points at the two length bytes) and returns its payload.
//
// A length that runs off the end of the data is clamped rather than rejected;
// a truncated final segment still tells us everything up to the truncation.
@(private)
jpeg_segment :: proc(data: []byte, pos: int) -> (payload: []byte, next: int, ok: bool) {
if pos + 2 > len(data) {
return nil, len(data), false
}
seglen := int(data[pos]) << 8 | int(data[pos + 1])
if seglen < 2 {
// Nonsense length. Skip the two bytes we read so the caller still
// makes progress and cannot spin here.
return nil, pos + 2, true
}
end := pos + seglen
if end > len(data) {
end = len(data)
}
return data[pos + 2:end], end, true
}
// jpeg_find_dnl looks ahead for a define-number-of-lines marker, used when the
// frame header left the height at zero. The scan is a plain forward search for
// a well-formed DNL segment; a false positive inside entropy data would need
// the four bytes FF DC 00 04, which the byte-stuffing rules make impossible
// for FF DC to be (0xDC is not a stuffed zero and not a restart marker), so
// this is safe in practice.
@(private)
jpeg_find_dnl :: proc(data: []byte, from: int) -> (lines: int, ok: bool) {
for i := max(from, 0); i + 5 < len(data); i += 1 {
if data[i] != 0xFF || data[i + 1] != JPEG_DNL { continue }
if data[i + 2] != 0x00 || data[i + 3] != 0x04 { continue }
n := int(data[i + 4]) << 8 | int(data[i + 5])
if n > 0 {
return n, true
}
}
return 0, false
}
// ---------------------------------------------------------------------------
// Decoder state
// ---------------------------------------------------------------------------
@(private)
Jpeg_Transform :: enum u8 {
None, // components are already in their final colour space
Ycbcr, // three components, inverse YCbCr
Ycck, // four components, inverse YCbCr on the first three, then invert
}
@(private)
Jpeg_Comp :: struct {
id: int,
h, v: int, // sampling factors, 1..4
tq: int, // quantisation table selector
dc_tbl: int,
ac_tbl: int,
dw, dh: int, // component size in samples
bw, bh: int, // component size in blocks, covering dw x dh
bw_pad: int, // blocks per line once padded out to whole MCUs
bh_pad: int,
pix_w: int, // bw_pad * 8
pix_h: int, // bh_pad * 8
coeffs: []i16,
samples: []byte,
pred: int, // running DC predictor for the scan in progress
}
@(private)
Jpeg_Scan :: struct {
comps: [4]int,
n: int,
ss, se: int,
ah, al: int,
}
@(private)
Jpeg_Decoder :: struct {
data: []byte,
pos: int,
scratch: mem.Allocator,
allocated: int,
width, height: int,
precision: int,
ncomp: int,
comps: [4]Jpeg_Comp,
hmax, vmax: int,
mcux, mcuy: int,
progressive: bool,
restart_interval: int,
qt: [4][64]u16,
qt_defined: [4]bool,
dc: [4]Jpeg_Huff,
ac: [4]Jpeg_Huff,
adobe_transform: int,
saw_adobe: bool,
saw_jfif: bool,
saw_sof: bool,
transform: Jpeg_Transform,
eobrun: int,
}
@(private)
jpeg_release :: proc(d: ^Jpeg_Decoder) {
for i in 0 ..< 4 {
if d.comps[i].coeffs != nil {
delete(d.comps[i].coeffs, d.scratch)
d.comps[i].coeffs = nil
}
if d.comps[i].samples != nil {
delete(d.comps[i].samples, d.scratch)
d.comps[i].samples = nil
}
}
}
// jpeg_charge reserves `n` bytes against the decode-wide allocation budget.
@(private)
jpeg_charge :: proc(d: ^Jpeg_Decoder, n: int) -> bool {
if n < 0 || n > MAX_JPEG_ALLOC - d.allocated {
return false
}
d.allocated += n
return true
}
// ---------------------------------------------------------------------------
// Header parsing and the top-level marker loop
// ---------------------------------------------------------------------------
@(private)
jpeg_run :: proc(d: ^Jpeg_Decoder, data: []byte) -> Error {
d.data = data
pos, found := jpeg_find_soi(data)
if !found {
return .Bad_Jpeg_Data
}
d.pos = pos
saw_scan := false
scans := 0
for _ in 0 ..< MAX_JPEG_SEGMENTS {
marker, next, ok := jpeg_next_marker(d.data, d.pos)
if !ok { break }
d.pos = next
switch marker {
case JPEG_SOI, JPEG_TEM:
continue
case JPEG_RST0, JPEG_RST0 + 1, JPEG_RST0 + 2, JPEG_RST0 + 3,
JPEG_RST0 + 4, JPEG_RST0 + 5, JPEG_RST0 + 6, JPEG_RST0 + 7:
// A restart marker outside a scan is leftover damage; step over it.
continue
case JPEG_EOI:
return jpeg_finish(d, saw_scan)
}
payload, after, pok := jpeg_segment(d.data, d.pos)
if !pok { break }
d.pos = after
switch marker {
case JPEG_DQT:
jpeg_read_dqt(d, payload)
case JPEG_DHT:
jpeg_read_dht(d, payload)
case JPEG_DRI:
if len(payload) >= 2 {
d.restart_interval = int(payload[0]) << 8 | int(payload[1])
}
case JPEG_APP0:
if len(payload) >= 5 && string(payload[:5]) == "JFIF\x00" {
d.saw_jfif = true
}
case JPEG_APP14:
// "Adobe", version, flags0, flags1, transform: 12 bytes.
if len(payload) >= 12 && string(payload[:5]) == "Adobe" {
d.saw_adobe = true
d.adobe_transform = int(payload[11])
}
case JPEG_DNL:
if len(payload) >= 2 && d.height == 0 {
d.height = int(payload[0]) << 8 | int(payload[1])
}
case JPEG_SOF0, JPEG_SOF1, JPEG_SOF2:
if d.saw_sof {
// A second frame header means a hierarchical or corrupt file.
// Keep the frame already decoded.
continue
}
d.progressive = marker == JPEG_SOF2
if ferr := jpeg_read_sof(d, payload); ferr != .None {
return ferr
}
case JPEG_SOF3, JPEG_SOF5, JPEG_SOF6, JPEG_SOF7,
JPEG_SOF9, JPEG_SOF10, JPEG_SOF11,
JPEG_SOF13, JPEG_SOF14, JPEG_SOF15, JPEG_DAC:
// Lossless, differential and arithmetic-coded frames. There is
// nothing to fall back to, so say so rather than emitting noise --
// unless a frame has already been decoded, in which case this is a
// corrupt byte that happened to look like a marker and the image
// in hand is worth more than the complaint.
if d.saw_sof {
continue
}
return .Unsupported_Image
case JPEG_SOS:
if !d.saw_sof {
return .Bad_Jpeg_Data
}
if scans >= MAX_JPEG_SCANS {
// Past this the file is spending the decoder's time rather
// than adding detail, so keep the image built so far and stop.
// Refinement scans only ever sharpen what is already there, so
// a legitimate file truncated here still looks right.
return jpeg_finish(d, saw_scan)
}
scan, serr := jpeg_read_sos(d, payload)
if serr != .None {
// An unusable scan header is not a reason to throw away the
// scans already decoded.
continue
}
saw_scan = true
scans += 1
jpeg_decode_scan(d, scan)
case:
// COM, APPn, JPGn, anything unknown: already stepped over.
}
}
return jpeg_finish(d, saw_scan)
}
@(private)
jpeg_finish :: proc(d: ^Jpeg_Decoder, saw_scan: bool) -> Error {
if !d.saw_sof {
return .Bad_Jpeg_Data
}
// A frame with no scan at all decodes to a uniform mid-grey image, which
// is more useful to a page than a hole where the image should be, but a
// file that never reached its first scan is damaged past the point of
// guessing.
if !saw_scan {
return .Bad_Jpeg_Data
}
return .None
}
@(private)
jpeg_read_sof :: proc(d: ^Jpeg_Decoder, payload: []byte) -> Error {
if len(payload) < 6 {
return .Bad_Jpeg_Data
}
d.precision = int(payload[0])
d.height = int(payload[1]) << 8 | int(payload[2])
d.width = int(payload[3]) << 8 | int(payload[4])
d.ncomp = int(payload[5])
if d.height == 0 {
if lines, ok := jpeg_find_dnl(d.data, d.pos); ok {
d.height = lines
}
}
if d.width <= 0 || d.height <= 0 {
return .Bad_Jpeg_Data
}
if d.ncomp < 1 || d.ncomp > 4 {
return .Bad_Jpeg_Data
}
if d.width > MAX_JPEG_PIXELS / d.height {
return .Bitmap_Too_Large
}
// 8 and 12 are the only precisions JPEG defines; anything else is damage.
// Deeper samples are shifted down at dequantisation so the rest of the
// pipeline stays 8-bit.
if d.precision < 8 || d.precision > 16 {
return .Bad_Jpeg_Data
}
if len(payload) < 6 + 3 * d.ncomp {
return .Bad_Jpeg_Data
}
d.hmax, d.vmax = 1, 1
for i in 0 ..< d.ncomp {
c := &d.comps[i]
base := 6 + 3 * i
c.id = int(payload[base])
c.h = int(payload[base + 1] >> 4)
c.v = int(payload[base + 1] & 15)
c.tq = int(payload[base + 2])
// Sampling factors outside 1..4 are illegal; clamping keeps the block
// geometry consistent instead of producing a zero-size plane.
if c.h < 1 || c.h > 4 { c.h = 1 }
if c.v < 1 || c.v > 4 { c.v = 1 }
if c.tq < 0 || c.tq > 3 { c.tq = 0 }
d.hmax = max(d.hmax, c.h)
d.vmax = max(d.vmax, c.v)
}
d.mcux = (d.width + 8 * d.hmax - 1) / (8 * d.hmax)
d.mcuy = (d.height + 8 * d.vmax - 1) / (8 * d.vmax)
for i in 0 ..< d.ncomp {
c := &d.comps[i]
c.dw = (d.width * c.h + d.hmax - 1) / d.hmax
c.dh = (d.height * c.v + d.vmax - 1) / d.vmax
c.bw = (c.dw + 7) / 8
c.bh = (c.dh + 7) / 8
c.bw_pad = d.mcux * c.h
c.bh_pad = d.mcuy * c.v
c.pix_w = c.bw_pad * 8
c.pix_h = c.bh_pad * 8
nblocks := c.bw_pad * c.bh_pad
if nblocks <= 0 || nblocks > MAX_JPEG_PIXELS / 64 {
return .Bitmap_Too_Large
}
if !jpeg_charge(d, nblocks * 64 * size_of(i16)) { return .Bitmap_Too_Large }
if !jpeg_charge(d, c.pix_w * c.pix_h) { return .Bitmap_Too_Large }
coef_err, samp_err: mem.Allocator_Error
c.coeffs, coef_err = make([]i16, nblocks * 64, d.scratch)
if coef_err != nil { return .Out_Of_Memory }
c.samples, samp_err = make([]byte, c.pix_w * c.pix_h, d.scratch)
if samp_err != nil { return .Out_Of_Memory }
// Undecoded blocks read back as mid-grey rather than black, so a
// truncated scan degrades into a faded image instead of a dark band.
for j in 0 ..< len(c.samples) {
c.samples[j] = 128
}
}
d.transform = jpeg_pick_transform(d)
d.saw_sof = true
return .None
}
// jpeg_pick_transform decides what colour transform to undo, following the
// same rules libjpeg does -- which is the de-facto standard, because every
// producer was tested against it.
@(private)
jpeg_pick_transform :: proc(d: ^Jpeg_Decoder) -> Jpeg_Transform {
switch d.ncomp {
case 3:
if d.saw_adobe {
// 0 means the components are already RGB.
return d.adobe_transform == 0 ? .None : .Ycbcr
}
if d.saw_jfif {
return .Ycbcr
}
// No marker to go on: component ids 'R','G','B' mean exactly that,
// and anything else (usually 1,2,3) is YCbCr.
if d.comps[0].id == 'R' && d.comps[1].id == 'G' && d.comps[2].id == 'B' {
return .None
}
return .Ycbcr
case 4:
if d.saw_adobe && d.adobe_transform == 2 {
return .Ycck
}
return .None
}
return .None
}
@(private)
jpeg_read_dqt :: proc(d: ^Jpeg_Decoder, payload: []byte) {
p := 0
for p < len(payload) {
pq := int(payload[p] >> 4)
tq := int(payload[p] & 15)
p += 1
if tq > 3 {
// Unusable selector: the rest of the segment cannot be located
// either, since table sizes depend on it.
return
}
n := pq == 0 ? 64 : 128
if p + n > len(payload) {
return
}
for k in 0 ..< 64 {
v: u16
if pq == 0 {
v = u16(payload[p + k])
} else {
v = u16(payload[p + 2 * k]) << 8 | u16(payload[p + 2 * k + 1])
}
// A zero quantiser would erase the coefficient; treat it as 1,
// which is what a viewer that survives such a file must do.
if v == 0 { v = 1 }
d.qt[tq][JPEG_ZIGZAG[k]] = v
}
d.qt_defined[tq] = true
p += n
}
}
@(private)
jpeg_read_dht :: proc(d: ^Jpeg_Decoder, payload: []byte) {
p := 0
for p + 17 <= len(payload) {
tc := int(payload[p] >> 4)
th := int(payload[p] & 15)
p += 1
counts := payload[p:p + 16]
p += 16
total := 0
for c in counts {
total += int(c)
}
if total > 256 || p + total > len(payload) {
return
}
vals := payload[p:p + total]
p += total
if th > 3 || tc > 1 {
continue
}
table := tc == 0 ? &d.dc[th] : &d.ac[th]
jpeg_huff_build(table, counts, vals)
}
}
@(private)
jpeg_read_sos :: proc(d: ^Jpeg_Decoder, payload: []byte) -> (scan: Jpeg_Scan, err: Error) {
if len(payload) < 1 {
return {}, .Bad_Jpeg_Data
}
ns := int(payload[0])
if ns < 1 || ns > 4 || len(payload) < 1 + 2 * ns + 3 {
return {}, .Bad_Jpeg_Data
}
for i in 0 ..< ns {
cs := int(payload[1 + 2 * i])
td := int(payload[2 + 2 * i] >> 4)
ta := int(payload[2 + 2 * i] & 15)
idx := -1
for j in 0 ..< d.ncomp {
if d.comps[j].id == cs {
idx = j
break
}
}
if idx < 0 {
// Component selectors that do not match the frame happen when a
// rewriter renumbers one header and not the other. Positional
// matching recovers those files.
if i < d.ncomp {
idx = i
} else {
return {}, .Bad_Jpeg_Data
}
}
scan.comps[i] = idx
d.comps[idx].dc_tbl = td > 3 ? 0 : td
d.comps[idx].ac_tbl = ta > 3 ? 0 : ta
}
scan.n = ns
tail := 1 + 2 * ns
scan.ss = int(payload[tail])
scan.se = int(payload[tail + 1])
scan.ah = int(payload[tail + 2] >> 4)
scan.al = int(payload[tail + 2] & 15)
if !d.progressive {
// A sequential scan covers the whole block whatever its header says.
scan.ss, scan.se, scan.ah, scan.al = 0, 63, 0, 0
} else {
if scan.ss > 63 { scan.ss = 63 }
if scan.se > 63 { scan.se = 63 }
if scan.se < scan.ss { scan.se = scan.ss }
if scan.al > 13 { scan.al = 13 }
if scan.ah > 13 { scan.ah = 13 }
// A DC scan is the only kind that may be interleaved, and it always
// covers coefficient 0 alone.
if scan.ss == 0 {
scan.se = 0
} else if ns != 1 {
// An interleaved AC scan is illegal; take its first component.
scan.n = 1
}
}
return scan, .None
}
// ---------------------------------------------------------------------------
// Bit reader
//
// Entropy-coded data is a bit stream with 0xFF escaped as 0xFF 0x00. Anything
// else after an 0xFF is a marker and ends the segment; from there on the
// reader hands out zero bits, which lets a truncated scan run to the end of
// its MCU loop and produce a partial image instead of failing.
// ---------------------------------------------------------------------------
@(private)
Jpeg_Bits :: struct {
data: []byte,
pos: int,
buf: u32,
cnt: int,
// Set once the reader has stopped at a marker. This is not the same as
// running out of data: the buffer is topped up several bits ahead of what
// the block decoders have consumed, so a scan that ends exactly on its
// last MCU still trips this while decoding perfectly.
marker_hit: bool,
marker_pos: int, // offset of the 0xFF that begins the pending marker
// Set when a request could not be met from real bits and was padded with
// zeroes. *This* is the "the data ran out" signal, and everything after it
// in the scan is guesswork.
starved: bool,
}
@(private)
jpeg_next_data_byte :: proc(b: ^Jpeg_Bits) -> (c: byte, ok: bool) {
if b.marker_hit {
return 0, false
}
if b.pos >= len(b.data) {
b.marker_hit = true
b.marker_pos = len(b.data)
return 0, false
}
v := b.data[b.pos]
if v != 0xFF {
b.pos += 1
return v, true
}
j := b.pos + 1
for j < len(b.data) && b.data[j] == 0xFF {
j += 1
}
if j >= len(b.data) {
b.marker_hit = true
b.marker_pos = b.pos
return 0, false
}
if b.data[j] == 0x00 {
b.pos = j + 1
return 0xFF, true
}
// Leave pos where it is: the scan loop needs to find this marker.
b.marker_hit = true
b.marker_pos = j - 1
return 0, false
}
// jpeg_fill_soft tops the bit buffer up to `n` bits if it can, and says how
// many it managed. It never reports starvation, because the Huffman decoder
// speculatively asks for a whole lookahead window and must not be told the
// data ran out merely because the last few codes of a scan are shorter than
// that window.
@(private)
jpeg_fill_soft :: proc(b: ^Jpeg_Bits, n: int) -> int {
for b.cnt < n {
c, ok := jpeg_next_data_byte(b)
if !ok { break }
b.buf = b.buf << 8 | u32(c)
b.cnt += 8
}
return b.cnt
}
// jpeg_get_bits consumes n bits, padding with zeroes and raising `starved`
// when the entropy data really has run out.
@(private)
jpeg_get_bits :: proc(b: ^Jpeg_Bits, n: int) -> i32 {
if n <= 0 { return 0 }
nn := min(n, 16)
if jpeg_fill_soft(b, nn) < nn {
b.buf <<= uint(nn - b.cnt)
b.cnt = nn
b.starved = true
}
b.cnt -= nn
return i32((b.buf >> uint(b.cnt)) & (1 << uint(nn) - 1))
}
// jpeg_take_restart consumes the RSTn marker the stream is sitting on.
//
// Only the caller knows when a marker is due, and it must only call this
// there. "The reader is parked on an FF Dn" is not by itself evidence of a
// segment boundary: the reader buffers whole bytes ahead of the bits that have
// been consumed and stops on the marker, so the last few blocks of an interval
// are routinely decoded while `pos` already points at the marker that follows
// them. Only the declared restart interval says which of those positions is
// the real boundary, which is why the DRI is what gates the call.
//
// The `cnt >= 8` test is a consistency check, not that evidence: with intact
// data every whole byte of the segment has been consumed by the time the
// interval is up, so all that can be left in the buffer is the encoder's
// byte-alignment padding. More than that means the block decoders and the
// encoder disagree about this segment, and the caller's forward search for the
// next marker is the better recovery.
@(private)
jpeg_take_restart :: proc(b: ^Jpeg_Bits) -> bool {
if b.cnt >= 8 {
return false
}
p := b.marker_hit ? b.marker_pos : b.pos
if p < 0 || p + 1 >= len(b.data) || b.data[p] != 0xFF {
return false
}
q := p + 1
for q < len(b.data) && b.data[q] == 0xFF {
q += 1
}
if q >= len(b.data) {
return false
}
m := b.data[q]
if m < JPEG_RST0 || m > JPEG_RST0 + 7 {
return false
}
b.pos = q + 1
b.marker_hit = false
b.marker_pos = 0
b.starved = false
b.buf = 0
b.cnt = 0
return true
}
// jpeg_bits_restart resynchronises at the next RSTn marker.
//
// It searches forward rather than insisting the marker is at the current
// position, because a restart interval that disagrees with the markers
// actually present is a common form of damage and the whole point of restart
// markers is that a decoder can find its place again. The search only ever
// moves forward, so the total cost across a scan stays linear.
@(private)
jpeg_bits_restart :: proc(b: ^Jpeg_Bits) -> bool {
b.buf = 0
b.cnt = 0
p := b.pos
for p + 1 < len(b.data) {
if b.data[p] != 0xFF {
p += 1
continue
}
m := b.data[p + 1]
if m == 0x00 || m == 0xFF {
p += 1
continue
}
if m >= JPEG_RST0 && m <= JPEG_RST0 + 7 {
b.pos = p + 2
b.marker_hit = false
b.marker_pos = 0
b.starved = false
return true
}
b.marker_hit = true
b.marker_pos = p
return false
}
b.pos = len(b.data)
b.marker_hit = true
b.marker_pos = len(b.data)
return false
}
// ---------------------------------------------------------------------------
// Huffman tables
// ---------------------------------------------------------------------------
@(private) JPEG_LOOKAHEAD :: 8
@(private)
Jpeg_Huff :: struct {
// Canonical code bounds per code length, as in ISO/IEC 10918-1 F.2.2.3.
mincode: [17]i32,
maxcode: [17]i32, // -1 where no code of that length exists
valptr: [17]i32,
vals: [256]byte,
nvals: int,
// Codes of JPEG_LOOKAHEAD bits or fewer resolve in one table read; longer
// ones fall back to the bit-at-a-time loop. Short codes dominate, so this
// is where nearly all the decoding time goes.
look_nbits: [1 << JPEG_LOOKAHEAD]u8,
look_sym: [1 << JPEG_LOOKAHEAD]byte,
defined: bool,
}
@(private)
jpeg_huff_build :: proc(h: ^Jpeg_Huff, counts: []byte, vals: []byte) {
h^ = {}
if len(counts) < 16 { return }
total := 0
for i in 0 ..< 16 {
total += int(counts[i])
}
if total == 0 || total > 256 || total > len(vals) {
return
}
copy(h.vals[:], vals[:total])
h.nvals = total
code := i32(0)
p := i32(0)
for l in 1 ..= 16 {
n := i32(counts[l - 1])
h.valptr[l] = p
h.mincode[l] = code
code += n
p += n
h.maxcode[l] = n == 0 ? -1 : code - 1
code <<= 1
}
idx := 0
for l in 1 ..= JPEG_LOOKAHEAD {
n := int(counts[l - 1])
for i in 0 ..< n {
if idx >= total { break }
base := int(h.mincode[l]) + i
shift := uint(JPEG_LOOKAHEAD - l)
lo := base << shift
hi := lo + (1 << shift)
// An over-subscribed table (more codes than the length allows)
// pushes entries past the end of the lookup window; drop those
// rather than refusing the whole table.
if lo >= 0 && hi <= 1 << JPEG_LOOKAHEAD {
for k in lo ..< hi {
h.look_nbits[k] = u8(l)
h.look_sym[k] = h.vals[idx]
}
}
idx += 1
}
}
h.defined = true
}
// jpeg_huff_decode reads one Huffman-coded symbol.
//
// An undefined table, or a code that no length matches, yields 0. For an AC
// table 0 is end-of-block and for a DC table it is a zero difference, so
// corrupt data ends the block quietly instead of running away.
@(private)
jpeg_huff_decode :: proc(b: ^Jpeg_Bits, h: ^Jpeg_Huff) -> int {
if !h.defined {
return 0
}
if jpeg_fill_soft(b, JPEG_LOOKAHEAD) >= JPEG_LOOKAHEAD {
look := int((b.buf >> uint(b.cnt - JPEG_LOOKAHEAD)) & (1 << JPEG_LOOKAHEAD - 1))
if nb := int(h.look_nbits[look]); nb != 0 {
b.cnt -= nb
return int(h.look_sym[look])
}
}
// Either a code longer than the lookahead window, or the last few bits of
// a scan. Both fall back to reading a bit at a time.
code := i32(0)
for l in 1 ..= 16 {
code = code << 1 | jpeg_get_bits(b, 1)
if code <= h.maxcode[l] {
idx := int(h.valptr[l] + code - h.mincode[l])
if idx < 0 || idx >= h.nvals {
return 0
}
return int(h.vals[idx])
}
}
return 0
}
// jpeg_extend turns an s-bit magnitude into the signed difference it stands
// for (ISO/IEC 10918-1 F.2.2.1).
@(private)
jpeg_extend :: #force_inline proc "contextless" (v: i32, s: int) -> i32 {
if s <= 0 { return 0 }
return v < 1 << uint(s - 1) ? v - (1 << uint(s)) + 1 : v
}
@(private)
jpeg_receive_extend :: #force_inline proc (b: ^Jpeg_Bits, s: int) -> i32 {
if s <= 0 || s > 16 { return 0 }
return jpeg_extend(jpeg_get_bits(b, s), s)
}
// ---------------------------------------------------------------------------
// Scan decoding
// ---------------------------------------------------------------------------
@(private)
jpeg_reset_scan_state :: proc(d: ^Jpeg_Decoder, scan: Jpeg_Scan) {
for i in 0 ..< scan.n {
d.comps[scan.comps[i]].pred = 0
}
d.eobrun = 0
}
@(private)
jpeg_decode_scan :: proc(d: ^Jpeg_Decoder, scan: Jpeg_Scan) {
if d.pos >= len(d.data) { return }
b := Jpeg_Bits{data = d.data, pos = d.pos}
jpeg_reset_scan_state(d, scan)
mcus_x, mcus_y: int
if scan.n == 1 {
c := &d.comps[scan.comps[0]]
mcus_x, mcus_y = c.bw, c.bh
} else {
mcus_x, mcus_y = d.mcux, d.mcuy
}
total := mcus_x * mcus_y
if total <= 0 {
d.pos = b.pos
return
}
restart_left := d.restart_interval
// `mcu` is stepped by hand rather than with a range so the restart paths
// can be taken before the block decoders run. It only ever increases, so
// the loop is bounded by the MCU count whatever the data does.
for mcu := 0; mcu < total; mcu += 1 {
// The declared interval, not the bit reader's position, is what says a
// marker is due. The reader buffers whole bytes ahead of the bits the
// block decoders have consumed and stops dead on a marker, so from the
// moment the last byte of a segment is in the buffer it is already
// "sitting on" the FF Dn while that segment's final blocks are still
// undecoded -- which for anything low-entropy (a scanned page, or the
// one-bit-per-block DC refinement scan every progressive file has) is
// most of the segment. Taking the marker on that evidence resets the
// DC predictors and the EOB run a block or more early and decodes the
// rest of the interval out of the next segment's bits.
due := d.restart_interval > 0 && restart_left <= 0
if due || b.starved {
// A marker is due, or the bits ran out mid-segment because the
// entropy data is corrupt or the DRI disagrees with the markers
// the file actually carries. Consume the marker where it is due;
// otherwise search forward for the next one, which is recoverable
// if any remains. If none does, everything from here stays at
// zero, which reconstructs as flat mid-grey.
if !(due && jpeg_take_restart(&b)) {
if !jpeg_bits_restart(&b) { break }
}
jpeg_reset_scan_state(d, scan)
restart_left = d.restart_interval
}
restart_left -= 1
my := mcu / mcus_x
mx := mcu % mcus_x
if scan.n == 1 {
ci := scan.comps[0]
jpeg_decode_block(d, &b, scan, ci, my, mx)
} else {
for i in 0 ..< scan.n {
ci := scan.comps[i]
comp := &d.comps[ci]
for by in 0 ..< comp.v {
for bx in 0 ..< comp.h {
jpeg_decode_block(d, &b, scan, ci, my * comp.v + by, mx * comp.h + bx)
}
}
}
}
}
d.pos = b.marker_hit ? b.marker_pos : b.pos
if d.pos < 0 || d.pos > len(d.data) {
d.pos = len(d.data)
}
}
@(private)
jpeg_decode_block :: proc(d: ^Jpeg_Decoder, b: ^Jpeg_Bits, scan: Jpeg_Scan, ci, brow, bcol: int) {
comp := &d.comps[ci]
if brow < 0 || bcol < 0 || brow >= comp.bh_pad || bcol >= comp.bw_pad {
return
}
base := (brow * comp.bw_pad + bcol) * 64
if base < 0 || base + 64 > len(comp.coeffs) {
return
}
coef := comp.coeffs[base:base + 64]
if !d.progressive {
jpeg_decode_baseline_block(d, b, comp, coef)
return
}
if scan.ss == 0 {
if scan.ah == 0 {
jpeg_decode_dc_first(d, b, comp, coef, scan.al)
} else {
jpeg_decode_dc_refine(b, coef, scan.al)
}
} else {
if scan.ah == 0 {
jpeg_decode_ac_first(d, b, comp, coef, scan)
} else {
jpeg_decode_ac_refine(d, b, comp, coef, scan)
}
}
}
@(private)
jpeg_store_dc :: #force_inline proc "contextless" (coef: []i16, v: i32) {
coef[0] = i16(clamp(v, i32(min(i16)), i32(max(i16))))
}
@(private)
jpeg_decode_baseline_block :: proc(d: ^Jpeg_Decoder, b: ^Jpeg_Bits, comp: ^Jpeg_Comp, coef: []i16) {
s := jpeg_huff_decode(b, &d.dc[comp.dc_tbl])
diff := jpeg_receive_extend(b, s)
comp.pred = int(clamp(i32(comp.pred) + diff, -1 << 20, 1 << 20))
jpeg_store_dc(coef, i32(comp.pred))
ac := &d.ac[comp.ac_tbl]
k := 1
for k < 64 {
rs := jpeg_huff_decode(b, ac)
r := rs >> 4
s2 := rs & 15
if s2 == 0 {
if r != 15 { break }
k += 16
continue
}
k += r
if k > 63 { break }
coef[JPEG_ZIGZAG[k]] = i16(clamp(jpeg_receive_extend(b, s2), i32(min(i16)), i32(max(i16))))
k += 1
}
}
@(private)
jpeg_decode_dc_first :: proc(
d: ^Jpeg_Decoder, b: ^Jpeg_Bits, comp: ^Jpeg_Comp, coef: []i16, al: int,
) {
s := jpeg_huff_decode(b, &d.dc[comp.dc_tbl])
diff := jpeg_receive_extend(b, s)
comp.pred = int(clamp(i32(comp.pred) + diff, -1 << 20, 1 << 20))
jpeg_store_dc(coef, i32(comp.pred) << uint(al))
}
@(private)
jpeg_decode_dc_refine :: proc(b: ^Jpeg_Bits, coef: []i16, al: int) {
if jpeg_get_bits(b, 1) != 0 {
coef[0] |= i16(1) << uint(al)
}
}
@(private)
jpeg_decode_ac_first :: proc(
d: ^Jpeg_Decoder, b: ^Jpeg_Bits, comp: ^Jpeg_Comp, coef: []i16, scan: Jpeg_Scan,
) {
if d.eobrun > 0 {
d.eobrun -= 1
return
}
ac := &d.ac[comp.ac_tbl]
k := scan.ss
for k <= scan.se {
rs := jpeg_huff_decode(b, ac)
r := rs >> 4
s := rs & 15
if s == 0 {
if r != 15 {
d.eobrun = 1 << uint(r)
if r > 0 {
d.eobrun += int(jpeg_get_bits(b, r))
}
d.eobrun -= 1
break
}
k += 16
continue
}
k += r
if k > scan.se || k > 63 { break }
v := jpeg_receive_extend(b, s) << uint(scan.al)
coef[JPEG_ZIGZAG[k]] = i16(clamp(v, i32(min(i16)), i32(max(i16))))
k += 1
}
}
// jpeg_decode_ac_refine appends correction bits to the AC coefficients of one
// block (ISO/IEC 10918-1 G.1.2.3). This is the fiddliest part of progressive
// JPEG: the bit stream interleaves newly-nonzero coefficients with correction
// bits for coefficients that earlier scans already made nonzero, and the run
// length counts only the ones that are still zero.
@(private)
jpeg_decode_ac_refine :: proc(
d: ^Jpeg_Decoder, b: ^Jpeg_Bits, comp: ^Jpeg_Comp, coef: []i16, scan: Jpeg_Scan,
) {
p1 := i16(1) << uint(scan.al)
m1 := -(i16(1) << uint(scan.al))
ac := &d.ac[comp.ac_tbl]
k := scan.ss
if d.eobrun == 0 {
for k <= scan.se {
rs := jpeg_huff_decode(b, ac)
r := rs >> 4
s := rs & 15
newval := i16(0)
if s != 0 {
// The magnitude of a newly nonzero coefficient is always 1 in
// a refinement scan; a larger one is corrupt, and taking the
// sign bit anyway keeps the bit stream aligned.
newval = jpeg_get_bits(b, 1) != 0 ? p1 : m1
} else if r != 15 {
d.eobrun = 1 << uint(r)
if r > 0 {
d.eobrun += int(jpeg_get_bits(b, r))
}
break
}
for {
pos := int(JPEG_ZIGZAG[k])
if coef[pos] != 0 {
if jpeg_get_bits(b, 1) != 0 && coef[pos] & p1 == 0 {
coef[pos] += coef[pos] >= 0 ? p1 : m1
}
} else {
r -= 1
if r < 0 { break }
}
k += 1
if k > scan.se { break }
}
if newval != 0 && k <= scan.se {
coef[JPEG_ZIGZAG[k]] = newval
}
k += 1
}
}
if d.eobrun > 0 {
for ; k <= scan.se; k += 1 {
pos := int(JPEG_ZIGZAG[k])
if coef[pos] != 0 {
if jpeg_get_bits(b, 1) != 0 && coef[pos] & p1 == 0 {
coef[pos] += coef[pos] >= 0 ? p1 : m1
}
}
}
d.eobrun -= 1
}
}
// ---------------------------------------------------------------------------
// Inverse DCT
//
// The accurate integer IDCT from ISO/IEC 10918-1's own reference software,
// after Loeffler, Ligtenberg and Moschytz: 11 multiplies and 29 adds per
// one-dimensional pass, in 13-bit fixed point. Two passes, columns then rows.
// A separable pair of one-dimensional transforms is O(n^3) for the block
// against the O(n^4) of the definition, and this one is a further constant
// factor faster again.
// ---------------------------------------------------------------------------
@(private) JPEG_CONST_BITS :: 13
@(private) JPEG_PASS1_BITS :: 2
@(private) JPEG_FIX_0_298631336 :: 2446
@(private) JPEG_FIX_0_390180644 :: 3196
@(private) JPEG_FIX_0_541196100 :: 4433
@(private) JPEG_FIX_0_765366865 :: 6270
@(private) JPEG_FIX_0_899976223 :: 7373
@(private) JPEG_FIX_1_175875602 :: 9633
@(private) JPEG_FIX_1_501321110 :: 12299
@(private) JPEG_FIX_1_847759065 :: 15137
@(private) JPEG_FIX_1_961570560 :: 16069
@(private) JPEG_FIX_2_053119869 :: 16819
@(private) JPEG_FIX_2_562915447 :: 20995
@(private) JPEG_FIX_3_072711026 :: 25172
// Dequantised coefficients are clamped to this. Valid 8-bit data never gets
// near it (the DCT of an 8-bit block bounds every coefficient by 1024), and
// the clamp is what keeps a corrupt coefficient from overflowing the 32-bit
// fixed-point arithmetic below into visible noise.
@(private) JPEG_COEF_LIMIT :: 1 << 14
@(private)
jpeg_descale :: #force_inline proc "contextless" (x: i32, n: uint) -> i32 {
return (x + (1 << (n - 1))) >> n
}
@(private)
jpeg_clamp_sample :: #force_inline proc "contextless" (x: i32) -> byte {
if x < 0 { return 0 }
if x > 255 { return 255 }
return byte(x)
}
@(private)
jpeg_idct_block :: proc(coef: []i16, qt: ^[64]u16, dst: []byte, off, stride: int, shift: uint) {
dq :: #force_inline proc "contextless" (c: i16, q: u16, sh: uint) -> i32 {
return clamp((i32(c) * i32(q)) >> sh, -JPEG_COEF_LIMIT, JPEG_COEF_LIMIT)
}
ws: [64]i32
for col in 0 ..< 8 {
if coef[col + 8] == 0 && coef[col + 16] == 0 && coef[col + 24] == 0 &&
coef[col + 32] == 0 && coef[col + 40] == 0 && coef[col + 48] == 0 &&
coef[col + 56] == 0 {
// An all-zero AC column is the common case in flat areas.
dcval := dq(coef[col], qt[col], shift) << JPEG_PASS1_BITS
for row in 0 ..< 8 {
ws[row * 8 + col] = dcval
}
continue
}
z2 := dq(coef[col + 16], qt[col + 16], shift)
z3 := dq(coef[col + 48], qt[col + 48], shift)
z1 := (z2 + z3) * JPEG_FIX_0_541196100
tmp2 := z1 + z3 * -JPEG_FIX_1_847759065
tmp3 := z1 + z2 * JPEG_FIX_0_765366865
z2 = dq(coef[col], qt[col], shift)
z3 = dq(coef[col + 32], qt[col + 32], shift)
tmp0 := (z2 + z3) << JPEG_CONST_BITS
tmp1 := (z2 - z3) << JPEG_CONST_BITS
tmp10 := tmp0 + tmp3
tmp13 := tmp0 - tmp3
tmp11 := tmp1 + tmp2
tmp12 := tmp1 - tmp2
tmp0 = dq(coef[col + 56], qt[col + 56], shift)
tmp1 = dq(coef[col + 40], qt[col + 40], shift)
tmp2 = dq(coef[col + 24], qt[col + 24], shift)
tmp3 = dq(coef[col + 8], qt[col + 8], shift)
z1 = tmp0 + tmp3
z2 = tmp1 + tmp2
z3 = tmp0 + tmp2
z4 := tmp1 + tmp3
z5 := (z3 + z4) * JPEG_FIX_1_175875602
tmp0 *= JPEG_FIX_0_298631336
tmp1 *= JPEG_FIX_2_053119869
tmp2 *= JPEG_FIX_3_072711026
tmp3 *= JPEG_FIX_1_501321110
z1 *= -JPEG_FIX_0_899976223
z2 *= -JPEG_FIX_2_562915447
z3 *= -JPEG_FIX_1_961570560
z4 *= -JPEG_FIX_0_390180644
z3 += z5
z4 += z5
tmp0 += z1 + z3
tmp1 += z2 + z4
tmp2 += z2 + z3
tmp3 += z1 + z4
ws[col] = jpeg_descale(tmp10 + tmp3, JPEG_CONST_BITS - JPEG_PASS1_BITS)
ws[56 + col] = jpeg_descale(tmp10 - tmp3, JPEG_CONST_BITS - JPEG_PASS1_BITS)
ws[8 + col] = jpeg_descale(tmp11 + tmp2, JPEG_CONST_BITS - JPEG_PASS1_BITS)
ws[48 + col] = jpeg_descale(tmp11 - tmp2, JPEG_CONST_BITS - JPEG_PASS1_BITS)
ws[16 + col] = jpeg_descale(tmp12 + tmp1, JPEG_CONST_BITS - JPEG_PASS1_BITS)
ws[40 + col] = jpeg_descale(tmp12 - tmp1, JPEG_CONST_BITS - JPEG_PASS1_BITS)
ws[24 + col] = jpeg_descale(tmp13 + tmp0, JPEG_CONST_BITS - JPEG_PASS1_BITS)
ws[32 + col] = jpeg_descale(tmp13 - tmp0, JPEG_CONST_BITS - JPEG_PASS1_BITS)
}
for row in 0 ..< 8 {
w := ws[row * 8:row * 8 + 8]
out := off + row * stride
if out < 0 || out + 8 > len(dst) {
continue
}
z2 := w[2]
z3 := w[6]
z1 := (z2 + z3) * JPEG_FIX_0_541196100
tmp2 := z1 + z3 * -JPEG_FIX_1_847759065
tmp3 := z1 + z2 * JPEG_FIX_0_765366865
tmp0 := (w[0] + w[4]) << JPEG_CONST_BITS
tmp1 := (w[0] - w[4]) << JPEG_CONST_BITS
tmp10 := tmp0 + tmp3
tmp13 := tmp0 - tmp3
tmp11 := tmp1 + tmp2
tmp12 := tmp1 - tmp2
tmp0 = w[7]
tmp1 = w[5]
tmp2 = w[3]
tmp3 = w[1]
z1 = tmp0 + tmp3
z2 = tmp1 + tmp2
z3 = tmp0 + tmp2
z4 := tmp1 + tmp3
z5 := (z3 + z4) * JPEG_FIX_1_175875602
tmp0 *= JPEG_FIX_0_298631336
tmp1 *= JPEG_FIX_2_053119869
tmp2 *= JPEG_FIX_3_072711026
tmp3 *= JPEG_FIX_1_501321110
z1 *= -JPEG_FIX_0_899976223
z2 *= -JPEG_FIX_2_562915447
z3 *= -JPEG_FIX_1_961570560
z4 *= -JPEG_FIX_0_390180644
z3 += z5
z4 += z5
tmp0 += z1 + z3
tmp1 += z2 + z4
tmp2 += z2 + z3
tmp3 += z1 + z4
// The level shift of ISO/IEC 10918-1 A.3.1: samples are coded centred
// on zero and stored centred on 128.
final :: JPEG_CONST_BITS + JPEG_PASS1_BITS + 3
dst[out] = jpeg_clamp_sample(jpeg_descale(tmp10 + tmp3, final) + 128)
dst[out + 7] = jpeg_clamp_sample(jpeg_descale(tmp10 - tmp3, final) + 128)
dst[out + 1] = jpeg_clamp_sample(jpeg_descale(tmp11 + tmp2, final) + 128)
dst[out + 6] = jpeg_clamp_sample(jpeg_descale(tmp11 - tmp2, final) + 128)
dst[out + 2] = jpeg_clamp_sample(jpeg_descale(tmp12 + tmp1, final) + 128)
dst[out + 5] = jpeg_clamp_sample(jpeg_descale(tmp12 - tmp1, final) + 128)
dst[out + 3] = jpeg_clamp_sample(jpeg_descale(tmp13 + tmp0, final) + 128)
dst[out + 4] = jpeg_clamp_sample(jpeg_descale(tmp13 - tmp0, final) + 128)
}
}
// ---------------------------------------------------------------------------
// Output: inverse DCT, upsampling, colour transform
// ---------------------------------------------------------------------------
@(private)
jpeg_idct_planes :: proc(d: ^Jpeg_Decoder) {
// Precisions above 8 bits are shifted down here rather than carried
// through the whole pipeline: the output is bytes either way, and doing it
// at dequantisation keeps the fixed-point IDCT inside its designed range.
shift := uint(max(d.precision - 8, 0))
for i in 0 ..< d.ncomp {
c := &d.comps[i]
qt := &d.qt[c.tq]
for brow in 0 ..< c.bh_pad {
for bcol in 0 ..< c.bw_pad {
base := (brow * c.bw_pad + bcol) * 64
if base + 64 > len(c.coeffs) { continue }
jpeg_idct_block(
c.coeffs[base:base + 64], qt, c.samples,
brow * 8 * c.pix_w + bcol * 8, c.pix_w, shift,
)
}
}
}
}
@(private)
jpeg_ycc_to_rgb :: #force_inline proc "contextless" (y, cb, cr: i32) -> (r, g, b: byte) {
// Fixed point at 16 bits, matching the coefficients in ITU-T T.871 (JFIF)
// 7: R = Y + 1.402 Cr, G = Y - 0.344136 Cb - 0.714136 Cr, B = Y + 1.772 Cb.
cbv := cb - 128
crv := cr - 128
r = jpeg_clamp_sample(y + ((91881 * crv + 32768) >> 16))
g = jpeg_clamp_sample(y + ((-22554 * cbv - 46802 * crv + 32768) >> 16))
b = jpeg_clamp_sample(y + ((116130 * cbv + 32768) >> 16))
return
}
@(private)
jpeg_emit :: proc(d: ^Jpeg_Decoder, allocator: mem.Allocator) -> (out: []byte, err: Error) {
w, h, n := d.width, d.height, d.ncomp
if w <= 0 || h <= 0 || n <= 0 || n > 4 {
return nil, .Bad_Jpeg_Data
}
if w > MAX_JPEG_PIXELS / h {
return nil, .Bitmap_Too_Large
}
total := w * h * n
if !jpeg_charge(d, total) {
return nil, .Bitmap_Too_Large
}
jpeg_idct_planes(d)
out_err: mem.Allocator_Error
out, out_err = make([]byte, total, allocator)
if out_err != nil {
return nil, .Out_Of_Memory
}
// Component samples are replicated to full resolution rather than
// interpolated. Box upsampling is what ISO/IEC 10918-1's own informative
// decoder does; the smoother triangular filter libjpeg defaults to would
// change every subsampled pixel by a couple of levels and buys nothing at
// the scales a page is rasterised at.
if !jpeg_charge(d, w * n * size_of(i32)) {
delete(out, allocator)
return nil, .Bitmap_Too_Large
}
xmap, xerr := make([]i32, w * n, d.scratch)
if xerr != nil {
delete(out, allocator)
return nil, .Out_Of_Memory
}
defer delete(xmap, d.scratch)
for ci in 0 ..< n {
c := &d.comps[ci]
last := max(c.dw - 1, 0)
for x in 0 ..< w {
sx := x * c.h / d.hmax
xmap[ci * w + x] = i32(min(sx, last))
}
}
rows: [4][]byte
o := 0
for y in 0 ..< h {
for ci in 0 ..< n {
c := &d.comps[ci]
sy := min(y * c.v / d.vmax, max(c.dh - 1, 0))
start := sy * c.pix_w
if start < 0 || start + c.pix_w > len(c.samples) {
start = 0
}
rows[ci] = c.samples[start:start + c.pix_w]
}
switch {
case n == 1:
r0 := rows[0]
m0 := xmap[:w]
for x in 0 ..< w {
out[o] = r0[m0[x]]
o += 1
}
case n == 3 && d.transform == .Ycbcr:
r0, r1, r2 := rows[0], rows[1], rows[2]
m0 := xmap[:w]
m1 := xmap[w:2 * w]
m2 := xmap[2 * w:3 * w]
for x in 0 ..< w {
cr, cg, cb := jpeg_ycc_to_rgb(i32(r0[m0[x]]), i32(r1[m1[x]]), i32(r2[m2[x]]))
out[o], out[o + 1], out[o + 2] = cr, cg, cb
o += 3
}
case n == 4 && d.transform == .Ycck:
r0, r1, r2, r3 := rows[0], rows[1], rows[2], rows[3]
m0 := xmap[:w]
m1 := xmap[w:2 * w]
m2 := xmap[2 * w:3 * w]
m3 := xmap[3 * w:4 * w]
for x in 0 ..< w {
cr, cg, cb := jpeg_ycc_to_rgb(i32(r0[m0[x]]), i32(r1[m1[x]]), i32(r2[m2[x]]))
// YCCK stores the complement of CMY as if it were RGB, so the
// inverse transform ends by complementing back. This is not
// the separate whole-image inversion Adobe applies to the CMYK
// it puts in PDFs -- that one belongs to /Decode.
out[o] = 255 - cr
out[o + 1] = 255 - cg
out[o + 2] = 255 - cb
out[o + 3] = r3[m3[x]]
o += 4
}
case:
for x in 0 ..< w {
for ci in 0 ..< n {
out[o] = rows[ci][xmap[ci * w + x]]
o += 1
}
}
}
}
return out, .None
}
// ---------------------------------------------------------------------------
// Tables
// ---------------------------------------------------------------------------
// Zig-zag order to natural (row-major) order, ISO/IEC 10918-1 Figure A.6.
@(private, rodata)
JPEG_ZIGZAG := [64]u8{
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34,
27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63,
}