package pdf import "core:slice" import "core:testing" import "core:time" // Test vectors for the DCTDecode filter. // // The embedded files were produced with a throwaway Python script during // development. `grey8`, `grey16`, `greyprog`, `rgb444`, `rgb420`, `rgbprog`, // `cmyk`, `rst` and `norst` are Pillow (that is, libjpeg-turbo) encodings of // the synthetic images described beside each one; `dcflat` and `oddsamp` were // assembled byte by byte by a small encoder in the same script, because no // ordinary encoder will emit the sampling factors and single-coefficient // blocks they need. Everything else -- the YCCK file, the DNL file, the wrong // restart interval, the truncations -- is patched out of those at run time by // the helpers below, so that what makes each variant malformed is visible in // the test rather than buried in an opaque blob. // // Where a value is asserted exactly it was cross-checked against libjpeg: the // grey, 4:4:4 and CMYK files decode byte-for-byte identically to Pillow. The // chroma-subsampled files deliberately do not, because this decoder replicates // chroma samples where libjpeg interpolates them, so those are checked against // the source image with a tolerance instead. // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- @(private) jpeg_t_copy :: proc(src: []byte, extra := 0) -> []byte { out := make([]byte, len(src) + extra, context.allocator) copy(out, src) return out } // jpeg_t_seg walks the marker segments and returns the offset of the 0xFF that // starts the first segment with this marker code, or -1. @(private) jpeg_t_seg :: proc(data: []byte, marker: byte) -> int { p := 2 for p + 3 < len(data) { if data[p] != 0xFF { return -1 } m := data[p + 1] if m == marker { return p } if m == 0xD8 || m == 0xD9 || (m >= 0xD0 && m <= 0xD7) || m == 0x01 { p += 2 continue } seglen := int(data[p + 2]) << 8 | int(data[p + 3]) if seglen < 2 { return -1 } if m == 0xDA { return -1 } p += 2 + seglen } return -1 } @(private) jpeg_t_decode :: proc(t: ^testing.T, data: []byte, loc := #caller_location) -> Jpeg_Image { img, err := jpeg_decode(data, context.allocator) testing.expectf(t, err == .None, "decode failed: %v", err, loc = loc) return img } // jpeg_t_max_dev is the largest absolute difference between two same-length // sample buffers. @(private) jpeg_t_max_dev :: proc(a, b: []byte) -> int { worst := 0 n := min(len(a), len(b)) for i in 0 ..< n { d := int(a[i]) - int(b[i]) worst = max(worst, abs(d)) } return worst } // --------------------------------------------------------------------------- // Frame headers // --------------------------------------------------------------------------- @(test) test_jpeg_info :: proc(t: ^testing.T) { Case :: struct { name: string, data: []byte, w, h, n, bits: int, } cases := []Case{ {"grey8", JPEG_T_GREY8[:], 8, 8, 1, 8}, {"grey16", JPEG_T_GREY16[:], 16, 16, 1, 8}, {"greyprog", JPEG_T_GREYPROG[:], 16, 16, 1, 8}, {"rgb444", JPEG_T_RGB444[:], 16, 16, 3, 8}, {"rgb420", JPEG_T_RGB420[:], 16, 16, 3, 8}, {"rgbprog", JPEG_T_RGBPROG[:], 16, 16, 3, 8}, {"cmyk", JPEG_T_CMYK[:], 16, 16, 4, 8}, {"rst", JPEG_T_RST[:], 32, 16, 1, 8}, {"dcflat", JPEG_T_DCFLAT[:], 8, 8, 1, 8}, {"oddsamp", JPEG_T_ODDSAMP[:], 32, 8, 3, 8}, } for c in cases { w, h, n, bits, err := jpeg_info(c.data) testing.expectf(t, err == .None, "%s: %v", c.name, err) testing.expectf(t, w == c.w && h == c.h, "%s: size %dx%d, want %dx%d", c.name, w, h, c.w, c.h) testing.expectf(t, n == c.n, "%s: %d components, want %d", c.name, n, c.n) testing.expectf(t, bits == c.bits, "%s: %d bits, want %d", c.name, bits, c.bits) } } // --------------------------------------------------------------------------- // Baseline sequential // --------------------------------------------------------------------------- // A single 8x8 block whose only nonzero coefficient is the DC one, with an // all-ones quantisation table. The inverse DCT of such a block is flat at // S00/8 plus the level shift of 128 (ISO/IEC 10918-1, A.3.3 and A.3.1), so // S00 = 40 must reconstruct as exactly 133 everywhere. This pins the IDCT's // scaling and rounding against arithmetic rather than against another decoder. @(test) test_jpeg_dc_only_block :: proc(t: ^testing.T) { img := jpeg_t_decode(t, JPEG_T_DCFLAT[:]) defer delete(img.data, context.allocator) testing.expect(t, img.width == 8 && img.height == 8 && img.components == 1) testing.expect(t, img.bits == 8) testing.expect(t, img.adobe_transform == -1) for v, i in img.data { testing.expectf(t, v == 133, "sample %d is %d, want 133", i, v) } } // An 8x8 grey ramp. These are the exact bytes libjpeg produces for the same // file, which they should be: both decoders use the same integer IDCT. @(test) test_jpeg_grey_baseline_exact :: proc(t: ^testing.T) { img := jpeg_t_decode(t, JPEG_T_GREY8[:]) defer delete(img.data, context.allocator) testing.expect(t, img.width == 8 && img.height == 8 && img.components == 1) testing.expect(t, len(img.data) == 64) testing.expect(t, slice.equal(img.data, JPEG_T_GREY8_PIXELS[:])) } // 16x16, four blocks, a hard-edged 4x4 checkerboard: plenty of high-frequency // coefficients, so this exercises the AC path and the ringing around an edge. @(test) test_jpeg_grey_baseline_blocks :: proc(t: ^testing.T) { img := jpeg_t_decode(t, JPEG_T_GREY16[:]) defer delete(img.data, context.allocator) testing.expect(t, len(img.data) == 256) worst := 0 for y in 0 ..< 16 { for x in 0 ..< 16 { want := (x / 4 + y / 4) % 2 == 0 ? 255 : 0 worst = max(worst, abs(int(img.data[y * 16 + x]) - want)) } } // Quality 85 on an image that is nothing but block-aligned edges: the // measured worst case is 6 levels. testing.expectf(t, worst <= 8, "worst deviation from the source image is %d", worst) } @(test) test_jpeg_colour_444 :: proc(t: ^testing.T) { img := jpeg_t_decode(t, JPEG_T_RGB444[:]) defer delete(img.data, context.allocator) testing.expect(t, img.width == 16 && img.height == 16 && img.components == 3) // Source: R = 16x, G = 16y, B = 128, encoded at quality 90 with no // subsampling, so every channel round-trips within a few levels. worst := 0 for y in 0 ..< 16 { for x in 0 ..< 16 { o := (y * 16 + x) * 3 worst = max(worst, abs(int(img.data[o]) - x * 16)) worst = max(worst, abs(int(img.data[o + 1]) - y * 16)) worst = max(worst, abs(int(img.data[o + 2]) - 128)) } } testing.expectf(t, worst <= 6, "worst deviation from the source image is %d", worst) } @(test) test_jpeg_colour_420_subsampled :: proc(t: ^testing.T) { img := jpeg_t_decode(t, JPEG_T_RGB420[:]) defer delete(img.data, context.allocator) testing.expect(t, img.width == 16 && img.height == 16 && img.components == 3) worst := 0 for y in 0 ..< 16 { for x in 0 ..< 16 { o := (y * 16 + x) * 3 worst = max(worst, abs(int(img.data[o]) - x * 16)) worst = max(worst, abs(int(img.data[o + 1]) - y * 16)) worst = max(worst, abs(int(img.data[o + 2]) - 128)) } } // Half-resolution chroma on a 16-levels-per-pixel ramp cannot do better // than about half a chroma sample of error; measured worst case is 16. testing.expectf(t, worst <= 20, "worst deviation from the source image is %d", worst) } // Sampling factors that no ordinary encoder emits: luma 4x1, one chroma plane // at 1x1 and the other at 2x1, so the two chroma planes are upsampled by // different, non-power-of-two-per-axis ratios. Every block carries only a DC // coefficient, which makes each 8x8 region a flat colour that can be predicted // from the YCbCr definition alone. @(test) test_jpeg_odd_sampling_factors :: proc(t: ^testing.T) { img := jpeg_t_decode(t, JPEG_T_ODDSAMP[:]) defer delete(img.data, context.allocator) testing.expect(t, img.width == 32 && img.height == 8 && img.components == 3) // Luma is flat at 28/108/148/228 across four 8-pixel bands, Cb is flat at // 108 across the whole row, and Cr is 158 over the left half and 98 over // the right. Converting with the JFIF coefficients gives: want := [4][3]byte{ {70, 13, 0}, {150, 93, 73}, {106, 176, 113}, {186, 255, 193}, } for y in 0 ..< 8 { for x in 0 ..< 32 { o := (y * 32 + x) * 3 w := want[x / 8] testing.expectf(t, img.data[o] == w[0] && img.data[o + 1] == w[1] && img.data[o + 2] == w[2], "pixel %d,%d is %d,%d,%d, want %d,%d,%d", x, y, img.data[o], img.data[o + 1], img.data[o + 2], w[0], w[1], w[2]) } } } // --------------------------------------------------------------------------- // Progressive // --------------------------------------------------------------------------- // A progressive encoding carries the same quantised coefficients as its // baseline twin, just spread over several scans, so the two must reconstruct // to identical bytes. That makes this an exact test of spectral selection and // successive approximation, on both the DC and AC scans, without needing any // expected pixels of its own. @(test) test_jpeg_progressive_matches_baseline :: proc(t: ^testing.T) { grey_prog := jpeg_t_decode(t, JPEG_T_GREYPROG[:]) defer delete(grey_prog.data, context.allocator) grey_base := jpeg_t_decode(t, JPEG_T_GREY16[:]) defer delete(grey_base.data, context.allocator) testing.expect(t, slice.equal(grey_prog.data, grey_base.data), "progressive grey does not match its baseline twin") rgb_prog := jpeg_t_decode(t, JPEG_T_RGBPROG[:]) defer delete(rgb_prog.data, context.allocator) rgb_base := jpeg_t_decode(t, JPEG_T_RGB420[:]) defer delete(rgb_base.data, context.allocator) testing.expect(t, slice.equal(rgb_prog.data, rgb_base.data), "progressive colour does not match its baseline twin") } // A progressive file cut off after its first scans still has a usable DC pass, // so it must decode to a recognisable, blocky version of the image rather than // to an error. @(test) test_jpeg_progressive_truncated :: proc(t: ^testing.T) { full := jpeg_t_decode(t, JPEG_T_GREYPROG[:]) defer delete(full.data, context.allocator) for cut in 1 ..= 8 { n := len(JPEG_T_GREYPROG) * cut / 9 img, err := jpeg_decode(JPEG_T_GREYPROG[:n], context.allocator) defer delete(img.data, context.allocator) if err != .None { // Cutting inside the frame header legitimately leaves nothing. continue } testing.expect(t, img.width == 16 && img.height == 16) testing.expect(t, len(img.data) == 256) } } // --------------------------------------------------------------------------- // Four components: CMYK and YCCK // --------------------------------------------------------------------------- // The source image was CMYK (16x, 16y, 200, 30). Adobe writes the CMYK JPEGs // it embeds in PDFs with every sample complemented, and the image dictionary // carries /Decode [1 0 1 0 1 0 1 0] to undo it. The filter must therefore hand // back the complemented samples untouched -- if it "helpfully" inverted them // the /Decode array would invert them right back. @(test) test_jpeg_cmyk_is_not_inverted :: proc(t: ^testing.T) { img := jpeg_t_decode(t, JPEG_T_CMYK[:]) defer delete(img.data, context.allocator) testing.expect(t, img.components == 4) testing.expectf(t, img.adobe_transform == 0, "adobe_transform is %d, want 0", img.adobe_transform) worst := 0 for y in 0 ..< 16 { for x in 0 ..< 16 { o := (y * 16 + x) * 4 worst = max(worst, abs(int(img.data[o]) - (255 - x * 16))) worst = max(worst, abs(int(img.data[o + 1]) - (255 - y * 16))) worst = max(worst, abs(int(img.data[o + 2]) - 55)) worst = max(worst, abs(int(img.data[o + 3]) - 225)) } } testing.expectf(t, worst <= 4, "worst deviation from the stored samples is %d", worst) } // The same file with the Adobe transform byte changed from 0 to 2. Nothing // else about the bit stream changes, so the component samples are identical // and only the colour transform differs: the first three channels go through // the inverse YCbCr transform and are then complemented, which is part of the // YCCK definition and not the separate Adobe inversion above. @(test) test_jpeg_ycck :: proc(t: ^testing.T) { app14 := jpeg_t_seg(JPEG_T_CMYK[:], 0xEE) testing.expect(t, app14 > 0, "no APP14 segment") if app14 < 0 { return } patched := jpeg_t_copy(JPEG_T_CMYK[:]) defer delete(patched, context.allocator) patched[app14 + 4 + 11] = 2 plain := jpeg_t_decode(t, JPEG_T_CMYK[:]) defer delete(plain.data, context.allocator) ycck := jpeg_t_decode(t, patched) defer delete(ycck.data, context.allocator) testing.expectf(t, ycck.adobe_transform == 2, "adobe_transform is %d, want 2", ycck.adobe_transform) testing.expect(t, len(ycck.data) == len(plain.data)) worst := 0 for i := 0; i + 3 < len(plain.data); i += 4 { y := f64(plain.data[i]) cb := f64(plain.data[i + 1]) - 128 cr := f64(plain.data[i + 2]) - 128 r := 255 - clamp(y + 1.402 * cr, 0, 255) g := 255 - clamp(y - 0.344136 * cb - 0.714136 * cr, 0, 255) b := 255 - clamp(y + 1.772 * cb, 0, 255) worst = max(worst, abs(int(ycck.data[i]) - int(r + 0.5))) worst = max(worst, abs(int(ycck.data[i + 1]) - int(g + 0.5))) worst = max(worst, abs(int(ycck.data[i + 2]) - int(b + 0.5))) // K is never transformed. testing.expect(t, ycck.data[i + 3] == plain.data[i + 3]) } testing.expectf(t, worst <= 1, "YCCK transform differs from the definition by %d", worst) } // --------------------------------------------------------------------------- // Restart markers // --------------------------------------------------------------------------- // The same image encoded twice at the same quality, once with a restart marker // after every MCU and once without. Restart markers carry no information, so // the two must decode identically. @(test) test_jpeg_restart_markers :: proc(t: ^testing.T) { with_rst := jpeg_t_decode(t, JPEG_T_RST[:]) defer delete(with_rst.data, context.allocator) without := jpeg_t_decode(t, JPEG_T_NORST[:]) defer delete(without.data, context.allocator) testing.expect(t, jpeg_t_seg(JPEG_T_RST[:], 0xDD) > 0, "test file has no DRI segment") testing.expect(t, jpeg_t_seg(JPEG_T_NORST[:], 0xDD) < 0, "control file has a DRI segment") testing.expect(t, slice.equal(with_rst.data, without.data), "restart markers changed the decoded image") } // The same pair again, but over content whose MCUs cost only a few bits each: // horizontal bars at quality 90, which is what a scanned page of text looks // like to an encoder. That is the case a decoder gets wrong when it decides a // restart marker is due from the position of its byte reader instead of from // the declared interval. The reader buffers whole bytes ahead of the bits the // block decoders have taken and stops dead on a marker, so once an interval's // remaining blocks code to fewer than eight bits -- routine here, and certain // in the one-bit-per-block DC refinement scan that every progressive file has // -- the marker is already under the reader while blocks of that interval are // still undecoded. Taking it there resets the DC predictors and the EOB run // early and decodes the rest of the interval out of the next segment's bits. // // Both files decode byte-for-byte identically to libjpeg-turbo; before the // interval gated the take, the baseline pair differed in 192 of 4096 samples // and the progressive pair in 960, by up to 190. @(test) test_jpeg_restart_low_entropy_mcus :: proc(t: ^testing.T) { Case :: struct { name: string, with, wro: []byte, } cases := []Case{ {"baseline", JPEG_T_LOWENT_RST[:], JPEG_T_LOWENT[:]}, {"progressive", JPEG_T_LOWENTP_RST[:], JPEG_T_LOWENTP[:]}, } for c in cases { testing.expectf(t, jpeg_t_seg(c.with, 0xDD) > 0, "%s: test file has no DRI segment", c.name) testing.expectf(t, jpeg_t_seg(c.wro, 0xDD) < 0, "%s: control file has a DRI segment", c.name) with_rst := jpeg_t_decode(t, c.with) defer delete(with_rst.data, context.allocator) without := jpeg_t_decode(t, c.wro) defer delete(without.data, context.allocator) testing.expectf(t, with_rst.width == 64 && with_rst.height == 64, "%s: %dx%d", c.name, with_rst.width, with_rst.height) testing.expectf(t, slice.equal(with_rst.data, without.data), "%s: restart markers changed the decoded image (max deviation %d)", c.name, jpeg_t_max_dev(with_rst.data, without.data)) } } // A DRI that disagrees with the markers actually in the stream. // // This is damage, and unlike a truncation it is not repairable. The declared // interval is the only thing that says how many MCUs a segment covers, so once // it is wrong the decoder cannot tell which of the markers it can see ends the // segment it is in; believing the markers instead is what breaks every intact // file with a low-entropy interval. libjpeg does not recover these either -- // patching a real file's DRI from 1 to 3 leaves it reporting "extraneous bytes // before marker 0xd0" with 92% of its samples wrong. // // What is required is that the damage stays damage. The decode terminates, // hands back an image of the size the frame header declared rather than an // error, and does not corrupt what it had already decoded correctly: the first // MCU is ahead of the first restart marker, so no disagreement about the // interval can reach it. @(test) test_jpeg_restart_interval_wrong :: proc(t: ^testing.T) { dri := jpeg_t_seg(JPEG_T_RST[:], 0xDD) testing.expect(t, dri > 0) if dri < 0 { return } good := jpeg_t_decode(t, JPEG_T_RST[:]) defer delete(good.data, context.allocator) for claim in ([]int{0, 3, 7, 65535}) { patched := jpeg_t_copy(JPEG_T_RST[:]) defer delete(patched, context.allocator) patched[dri + 4] = byte(claim >> 8) patched[dri + 5] = byte(claim) img, err := jpeg_decode(patched, context.allocator) defer delete(img.data, context.allocator) testing.expectf(t, err == .None, "DRI %d: %v", claim, err) testing.expectf(t, img.width == 32 && img.height == 16, "DRI %d: %dx%d", claim, img.width, img.height) if len(img.data) != len(good.data) { continue } // The 8x8 block at the origin is MCU 0, decoded before the stream // reaches any restart marker at all. for y in 0 ..< 8 { for x in 0 ..< 8 { i := y * 32 + x testing.expectf(t, img.data[i] == good.data[i], "DRI %d: sample (%d,%d) of the first MCU is %d, want %d", claim, x, y, img.data[i], good.data[i]) } } } } // Restart markers are also a recovery point: with the entropy data of one // segment corrupted, everything after the next marker still decodes. @(test) test_jpeg_restart_resync_after_corruption :: proc(t: ^testing.T) { sos := -1 p := 2 for p + 3 < len(JPEG_T_RST) { if JPEG_T_RST[p] != 0xFF { break } m := JPEG_T_RST[p + 1] seglen := int(JPEG_T_RST[p + 2]) << 8 | int(JPEG_T_RST[p + 3]) if m == 0xDA { sos = p + 2 + seglen break } p += 2 + seglen } testing.expect(t, sos > 0) if sos < 0 { return } good := jpeg_t_decode(t, JPEG_T_RST[:]) defer delete(good.data, context.allocator) patched := jpeg_t_copy(JPEG_T_RST[:]) defer delete(patched, context.allocator) // Corrupt the first entropy-coded segment only. patched[sos] = 0x5A img, err := jpeg_decode(patched, context.allocator) defer delete(img.data, context.allocator) testing.expectf(t, err == .None, "%v", err) testing.expect(t, img.width == 32 && img.height == 16) // The bottom half of the image comes after several restart markers and // must be untouched by the damage at the top. testing.expect(t, slice.equal(img.data[32 * 8:], good.data[32 * 8:]), "damage in the first restart interval leaked past a restart marker") } // --------------------------------------------------------------------------- // Tolerated malformations // --------------------------------------------------------------------------- @(test) test_jpeg_garbage_before_soi :: proc(t: ^testing.T) { junk := "HTTP/1.1 200 OK\r\nContent-Type: image/jpeg\r\n\r\n" data := make([]byte, len(junk) + len(JPEG_T_GREY8), context.allocator) defer delete(data, context.allocator) copy(data, transmute([]byte)junk) copy(data[len(junk):], JPEG_T_GREY8[:]) img := jpeg_t_decode(t, data) defer delete(img.data, context.allocator) testing.expect(t, slice.equal(img.data, JPEG_T_GREY8_PIXELS[:])) } @(test) test_jpeg_missing_eoi :: proc(t: ^testing.T) { trimmed := JPEG_T_GREY8[:len(JPEG_T_GREY8) - 2] testing.expect(t, JPEG_T_GREY8[len(JPEG_T_GREY8) - 1] == 0xD9, "test file does not end in EOI") img := jpeg_t_decode(t, trimmed) defer delete(img.data, context.allocator) testing.expect(t, slice.equal(img.data, JPEG_T_GREY8_PIXELS[:]), "dropping EOI changed the image") } // Fill bytes between segments are legal (ISO/IEC 10918-1, B.1.1.2) and // stray bytes are not, but both turn up. Neither may derail the parse. @(test) test_jpeg_extra_bytes_between_markers :: proc(t: ^testing.T) { sof := jpeg_t_seg(JPEG_T_GREY8[:], 0xC0) testing.expect(t, sof > 0) if sof < 0 { return } data := make([]byte, len(JPEG_T_GREY8) + 4, context.allocator) defer delete(data, context.allocator) copy(data, JPEG_T_GREY8[:sof]) data[sof], data[sof + 1], data[sof + 2], data[sof + 3] = 0xFF, 0xFF, 0xFF, 0xFF copy(data[sof + 4:], JPEG_T_GREY8[sof:]) img := jpeg_t_decode(t, data) defer delete(img.data, context.allocator) testing.expect(t, slice.equal(img.data, JPEG_T_GREY8_PIXELS[:])) } // A frame header may leave the height at zero and let a DNL marker after the // first scan supply it (ISO/IEC 10918-1, B.2.2). Rare, but the format allows // it and a stream that was being generated on the fly may well use it. @(test) test_jpeg_dnl_height :: proc(t: ^testing.T) { sof := jpeg_t_seg(JPEG_T_GREY16[:], 0xC0) testing.expect(t, sof > 0) if sof < 0 { return } n := len(JPEG_T_GREY16) data := make([]byte, n + 6, context.allocator) defer delete(data, context.allocator) copy(data, JPEG_T_GREY16[:n - 2]) hi, lo := data[sof + 5], data[sof + 6] data[sof + 5], data[sof + 6] = 0, 0 copy(data[n - 2:], []byte{0xFF, 0xDC, 0x00, 0x04, hi, lo, 0xFF, 0xD9}) w, h, _, _, ierr := jpeg_info(data) testing.expect(t, ierr == .None) testing.expectf(t, w == 16 && h == 16, "jpeg_info reports %dx%d", w, h) img := jpeg_t_decode(t, data) defer delete(img.data, context.allocator) reference := jpeg_t_decode(t, JPEG_T_GREY16[:]) defer delete(reference.data, context.allocator) testing.expect(t, img.height == 16) testing.expect(t, slice.equal(img.data, reference.data)) } // A quantisation value of zero would erase its coefficient and, in a decoder // that divided rather than multiplied, divide by it. Treating it as 1 keeps // the image and cannot fault. @(test) test_jpeg_zero_quant_value :: proc(t: ^testing.T) { dqt := jpeg_t_seg(JPEG_T_GREY8[:], 0xDB) testing.expect(t, dqt > 0) if dqt < 0 { return } patched := jpeg_t_copy(JPEG_T_GREY8[:]) defer delete(patched, context.allocator) for i in 0 ..< 64 { patched[dqt + 5 + i] = 0 } img, err := jpeg_decode(patched, context.allocator) defer delete(img.data, context.allocator) testing.expectf(t, err == .None, "%v", err) testing.expect(t, len(img.data) == 64) } // A scan that stops in the middle must yield the part that decoded. The rows // that were fully covered before the cut have to be bit-exact: a decoder that // resynchronised by guessing would corrupt them too. @(test) test_jpeg_truncated_scan :: proc(t: ^testing.T) { good := jpeg_t_decode(t, JPEG_T_NORST[:]) defer delete(good.data, context.allocator) intact_rows := 0 for cut := len(JPEG_T_NORST) - 3; cut > 0; cut -= 1 { img, err := jpeg_decode(JPEG_T_NORST[:cut], context.allocator) defer delete(img.data, context.allocator) if err != .None { continue } testing.expectf(t, img.width == 32 && img.height == 16, "cut at %d: %dx%d", cut, img.width, img.height) testing.expectf(t, len(img.data) == 512, "cut at %d: %d bytes", cut, len(img.data)) rows := 0 for rows < 16 { lo, hi := rows * 32, (rows + 1) * 32 if !slice.equal(img.data[lo:hi], good.data[lo:hi]) { break } rows += 1 } intact_rows = max(intact_rows, rows) } // Trimming a byte or two off the end costs at most the last MCU row. testing.expectf(t, intact_rows >= 8, "a nearly-complete truncation only reproduced %d rows", intact_rows) } // --------------------------------------------------------------------------- // Refusals // --------------------------------------------------------------------------- @(test) test_jpeg_malformed :: proc(t: ^testing.T) { Case :: struct { name: string, data: []byte, want: Error, } // 12-bit-per-sample and unusual-but-legal headers belong in the tolerated // set; these are the ones with genuinely nothing to hand back. cases := []Case{ {"empty", {}, .Bad_Jpeg_Data}, {"no soi", {0x00, 0x01, 0x02, 0x03}, .Bad_Jpeg_Data}, {"soi only", {0xFF, 0xD8}, .Bad_Jpeg_Data}, {"soi eoi", {0xFF, 0xD8, 0xFF, 0xD9}, .Bad_Jpeg_Data}, {"truncated sof", {0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00}, .Bad_Jpeg_Data}, {"zero size", {0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x0B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xD9}, .Bad_Jpeg_Data}, {"no components", {0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x08, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0xFF, 0xD9}, .Bad_Jpeg_Data}, {"5 components", {0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x08, 0x08, 0x00, 0x08, 0x00, 0x08, 0x05, 0xFF, 0xD9}, .Bad_Jpeg_Data}, // 65535 x 65535 is 4.3 gigapixels. {"absurd size", {0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x0B, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xD9}, .Bitmap_Too_Large}, // SOF3 is lossless, SOF9 is arithmetic-coded: implementable, but no // producer emits either into a PDF. {"lossless", {0xFF, 0xD8, 0xFF, 0xC3, 0x00, 0x0B, 0x08, 0x00, 0x08, 0x00, 0x08, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xD9}, .Unsupported_Image}, {"arithmetic", {0xFF, 0xD8, 0xFF, 0xC9, 0x00, 0x0B, 0x08, 0x00, 0x08, 0x00, 0x08, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xD9}, .Unsupported_Image}, // A frame header with no scan at all. {"frame no scan", {0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x0B, 0x08, 0x00, 0x08, 0x00, 0x08, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xD9}, .Bad_Jpeg_Data}, } for c in cases { img, err := jpeg_decode(c.data, context.allocator) defer delete(img.data, context.allocator) testing.expectf(t, err == c.want, "%s: got %v, want %v", c.name, err, c.want) testing.expectf(t, img.data == nil, "%s: returned data despite %v", c.name, err) } } // --------------------------------------------------------------------------- // Scan count // --------------------------------------------------------------------------- // jpeg_t_bits is the bit writer the two synthetic files below share. It escapes // 0xFF the way an encoder must, so the entropy data can never be mistaken for a // marker. @(private = "file") Jpeg_T_Bits :: struct { out: [dynamic]byte, acc: u32, n: uint, } @(private = "file") jpeg_t_put :: proc(w: ^Jpeg_T_Bits, value: u32, count: uint) { for i := count; i > 0; i -= 1 { w.acc = w.acc << 1 | (value >> (i - 1)) & 1 w.n += 1 if w.n == 8 { b := byte(w.acc) append(&w.out, b) if b == 0xFF { append(&w.out, 0x00) } w.acc, w.n = 0, 0 } } } // jpeg_t_flush pads to a byte boundary with 1 bits, which is what ISO/IEC // 10918-1 F.1.2.3 requires of an encoder. @(private = "file") jpeg_t_flush :: proc(w: ^Jpeg_T_Bits) { for w.n != 0 { jpeg_t_put(w, 1, 1) } } @(private = "file") jpeg_t_seg_append :: proc(out: ^[dynamic]byte, marker: byte, payload: []byte) { n := len(payload) + 2 append(out, 0xFF, marker, byte(n >> 8), byte(n)) append(out, ..payload) } // jpeg_t_dc_scan_file builds a progressive JPEG of a single 8x8 block whose // scans are all DC-first scans of the same coefficient. Every scan overwrites // what the one before it wrote, so the sample the block reconstructs to names // the last scan that actually ran -- which is how the scan limit is observed // without timing anything. // // All the scans but the last code a DC of 8; the last codes 120. With an // all-ones quantisation table the block reconstructs flat at S00/8 + 128 // (ISO/IEC 10918-1, A.3.3), so the image is 129 everywhere if the last scan was // refused and 143 everywhere if it ran. @(private = "file") jpeg_t_dc_scan_file :: proc(nscans: int, allocator := context.allocator) -> []byte { out := make([dynamic]byte, allocator) append(&out, 0xFF, 0xD8) dqt: [65]byte for i in 1 ..< 65 { dqt[i] = 1 } jpeg_t_seg_append(&out, 0xDB, dqt[:]) // SOF2: 8 bits, 8x8, one 1x1 component using quantisation table 0. jpeg_t_seg_append(&out, 0xC2, []byte{8, 0, 8, 0, 8, 1, 1, 0x11, 0}) // DHT, DC table 0: eight codes of length 4, so magnitude category s is the // 4-bit code s. dht: [17]byte dht[0] = 0x00 dht[4] = 8 tbl := make([]byte, 25, context.temp_allocator) copy(tbl, dht[:]) for s in 0 ..< 8 { tbl[17 + s] = byte(s) } jpeg_t_seg_append(&out, 0xC4, tbl) for i in 0 ..< nscans { jpeg_t_seg_append(&out, 0xDA, []byte{1, 1, 0x00, 0, 0, 0x00}) w := Jpeg_T_Bits{out = make([dynamic]byte, context.temp_allocator)} if i == nscans - 1 { jpeg_t_put(&w, 7, 4) // category 7: 64..127 jpeg_t_put(&w, 120, 7) } else { jpeg_t_put(&w, 4, 4) // category 4: 8..15 jpeg_t_put(&w, 8, 4) } jpeg_t_flush(&w) append(&out, ..w.out[:]) } append(&out, 0xFF, 0xD9) return out[:] } // The scan count is bounded on its own, not just by the segment count. // // A progressive scan is not cheap per byte: one EOB run costs 18 bits and skips // up to 32767 blocks, so a scan whose entropy data is a handful of bytes still // drives the MCU loop over the whole component. Bounding only the number of // marker segments therefore bounds the work at 65536 sweeps of a frame that may // hold millions of blocks, which is minutes of CPU from a stream of a megabyte. @(test) test_jpeg_scan_count_is_capped :: proc(t: ^testing.T) { over := jpeg_t_dc_scan_file(MAX_JPEG_SCANS + 5, context.allocator) defer delete(over, context.allocator) img := jpeg_t_decode(t, over) defer delete(img.data, context.allocator) testing.expect(t, img.width == 8 && img.height == 8 && img.components == 1) for v, i in img.data { testing.expectf(t, v == 129, "sample %d is %d: a scan past the limit was decoded", i, v) } // The same construction inside the limit must decode every scan, so that // the assertion above is about the limit and not about the file. under := jpeg_t_dc_scan_file(3, context.allocator) defer delete(under, context.allocator) ok := jpeg_t_decode(t, under) defer delete(ok.data, context.allocator) for v, i in ok.data { testing.expectf(t, v == 143, "sample %d is %d: the last scan did not run", i, v) } } // jpeg_t_eob_dos_file is the shape that makes the scan count expensive: a large // single-component progressive frame whose every scan covers it with EOB runs. // Nine bytes of entropy data per scan visit 65536 blocks. @(private = "file") jpeg_t_eob_dos_file :: proc(side, nscans: int, allocator := context.allocator) -> []byte { out := make([dynamic]byte, allocator) append(&out, 0xFF, 0xD8) dqt: [65]byte for i in 1 ..< 65 { dqt[i] = 1 } jpeg_t_seg_append(&out, 0xDB, dqt[:]) jpeg_t_seg_append(&out, 0xC2, []byte{ 8, byte(side >> 8), byte(side), byte(side >> 8), byte(side), 1, 1, 0x11, 0, }) // DHT, AC table 0: two codes of length 4 for the symbols 0x00 (EOB0) and // 0xE0 (EOB14), so EOB14 is the 4-bit code 0b0001. dht: [17]byte dht[0] = 0x10 dht[4] = 2 tbl := make([]byte, 19, context.temp_allocator) copy(tbl, dht[:]) tbl[17], tbl[18] = 0x00, 0xE0 jpeg_t_seg_append(&out, 0xC4, tbl) blocks := ((side + 7) / 8) * ((side + 7) / 8) w := Jpeg_T_Bits{out = make([dynamic]byte, context.temp_allocator)} for left := blocks; left > 0; { // EOB14 covers 1<<14 blocks plus its 14 extra bits. run := min(left, 1 << 14) jpeg_t_put(&w, 1, 4) jpeg_t_put(&w, u32(run - (1 << 14)) & 0x3FFF, 14) left -= run } jpeg_t_flush(&w) body := w.out[:] for _ in 0 ..< nscans { jpeg_t_seg_append(&out, 0xDA, []byte{1, 1, 0x00, 1, 63, 0x00}) append(&out, ..body) } append(&out, 0xFF, 0xD9) return out[:] } @(test) test_jpeg_many_scans_terminate :: proc(t: ^testing.T) { data := jpeg_t_eob_dos_file(2048, 20000, context.allocator) defer delete(data, context.allocator) started := time.now() img, err := jpeg_decode(data, context.allocator) defer delete(img.data, context.allocator) ms := time.duration_milliseconds(time.since(started)) testing.expectf(t, err == .None, "%v", err) testing.expect(t, img.width == 2048 && img.height == 2048) // Not a performance bound: with the scan count unbounded this same file // takes about fifteen seconds, and at the largest frame the header may // declare it is closer to an hour. The limit is loose enough that a loaded // machine cannot trip it. testing.expectf(t, ms < 5000, "%d scans took %.0f ms", 20000, ms) } // --------------------------------------------------------------------------- // Fuzz-shaped // --------------------------------------------------------------------------- @(private) jpeg_t_corpus :: proc() -> [10][]byte { return [10][]byte{ JPEG_T_GREY8[:], JPEG_T_GREY16[:], JPEG_T_GREYPROG[:], JPEG_T_RGB420[:], JPEG_T_RGBPROG[:], JPEG_T_CMYK[:], JPEG_T_PROGRST[:], JPEG_T_ODDSAMP[:], JPEG_T_LOWENT_RST[:], JPEG_T_LOWENTP_RST[:], } } // Every prefix of every test file. The decoder must return either an error or // a correctly-sized image, and must not fault, hang or leak on any of them. @(test) test_jpeg_fuzz_truncation :: proc(t: ^testing.T) { corpus := jpeg_t_corpus() for src in corpus { w, h, n, _, ierr := jpeg_info(src) testing.expect(t, ierr == .None) for cut in 0 ..= len(src) { img, err := jpeg_decode(src[:cut], context.allocator) defer delete(img.data, context.allocator) if err != .None { testing.expect(t, img.data == nil) continue } testing.expectf(t, img.width == w && img.height == h && img.components == n, "cut at %d: %dx%dx%d, want %dx%dx%d", cut, img.width, img.height, img.components, w, h, n) testing.expect(t, len(img.data) == w * h * n) } } } // One flipped bit at every byte offset. Corruption in a header can legitimately // change the reported size or make the file unreadable; what it must never do // is produce a buffer whose length disagrees with the header the decoder acted // on, or take unbounded time. @(test) test_jpeg_fuzz_bitflip :: proc(t: ^testing.T) { corpus := jpeg_t_corpus() for src in corpus { scratch := jpeg_t_copy(src) defer delete(scratch, context.allocator) for i in 0 ..< len(src) { for bit in ([]uint{0, 3, 7}) { scratch[i] = src[i] ~ (1 << bit) img, err := jpeg_decode(scratch, context.allocator) if err == .None { testing.expectf(t, len(img.data) == img.width * img.height * img.components, "offset %d bit %d: %d bytes for %dx%dx%d", i, bit, len(img.data), img.width, img.height, img.components) testing.expect(t, img.width > 0 && img.height > 0) testing.expect(t, img.components >= 1 && img.components <= 4) } else { testing.expect(t, img.data == nil) } delete(img.data, context.allocator) } scratch[i] = src[i] } } } // A single decode of a few-hundred-byte fuzz case that runs longer than this // has stopped being slow and started being stuck. @(private = "file") JPEG_T_FUZZ_HANG_MS :: 5000.0 // Two bytes of a marker segment length turned into something degenerate -- too // short to hold the segment's own length field, or far longer than the file -- // at every segment in turn. The parser has to clamp to the data it has rather // than reading past the end of the buffer. // // The outcome is not predictable per mutation: a length that swallows the rest // of the file may still leave a decodable image, and a mangled SOF may not. // What is predictable is the shape of whatever comes back, so that is what is // asserted -- an error hands back nothing to free, and a success hands back a // buffer that matches the dimensions it reports. A decoder that allocated for // the old header and then wrote for a new one would fail here. @(test) test_jpeg_fuzz_segment_lengths :: proc(t: ^testing.T) { corpus := jpeg_t_corpus() for src in corpus { for i := 2; i + 3 < len(src); i += 1 { if src[i] != 0xFF || src[i + 1] == 0x00 || src[i + 1] == 0xFF { continue } for value in ([]int{0, 1, 2, 3, 0xFFFF}) { scratch := jpeg_t_copy(src) defer delete(scratch, context.allocator) scratch[i + 2] = byte(value >> 8) scratch[i + 3] = byte(value) started := time.now() img, err := jpeg_decode(scratch, context.allocator) defer delete(img.data, context.allocator) // Not a performance bound: the inputs are a few hundred bytes, // so a decode measured in seconds is a loop whose bound came // out of the mutated length field. The limit is loose enough // that a loaded machine cannot trip it. ms := time.duration_milliseconds(time.since(started)) testing.expectf(t, ms < JPEG_T_FUZZ_HANG_MS, "offset %d length %#x: decode took %.0f ms", i, value, ms) if err != .None { testing.expectf(t, img.data == nil, "offset %d length %#x: failed decode returned %d bytes", i, value, len(img.data)) continue } // Every dimension the decoder reports has to be one it // allocated for: this is the check that a segment length is // never able to make the buffer and the header disagree. testing.expectf(t, img.width > 0 && img.height > 0 && img.components >= 1 && img.components <= 4, "offset %d length %#x: decoded %dx%dx%d", i, value, img.width, img.height, img.components) testing.expectf(t, img.width <= MAX_JPEG_PIXELS / max(img.height, 1), "offset %d length %#x: %dx%d is past the pixel cap", i, value, img.width, img.height) testing.expectf(t, len(img.data) == img.width * img.height * img.components, "offset %d length %#x: %d bytes for %dx%dx%d", i, value, len(img.data), img.width, img.height, img.components) testing.expectf(t, img.bits == 8, "offset %d length %#x: decoded image claims %d bits", i, value, img.bits) } } } } // Progressive and restart markers together: a progressive file whose scans are // each broken into restart intervals. The end-of-band run counter is scan // state like the DC predictors and has to be cleared at every restart, which // is the thing this combination catches. @(test) test_jpeg_progressive_with_restarts :: proc(t: ^testing.T) { testing.expect(t, jpeg_t_seg(JPEG_T_PROGRST[:], 0xDD) > 0, "test file has no DRI segment") img := jpeg_t_decode(t, JPEG_T_PROGRST[:]) defer delete(img.data, context.allocator) plain := jpeg_t_decode(t, JPEG_T_GREY16[:]) defer delete(plain.data, context.allocator) testing.expect(t, slice.equal(img.data, plain.data), "progressive with restarts does not match the baseline encoding") } // 12-bit samples. The format allows them; almost nothing produces them, and a // PDF's /BitsPerComponent has nowhere to say so. `jpeg_info` reports the // precision the file declares so a caller can complain about it, while // `jpeg_decode` scales the samples down to the byte-per-sample buffer it // always returns. The file is one block with DC = 640 against an all-ones // quantisation table, which after the 4-bit downshift is the same flat 133 as // the 8-bit version. @(test) test_jpeg_12_bit :: proc(t: ^testing.T) { _, _, _, bits, err := jpeg_info(JPEG_T_BITS12[:]) testing.expect(t, err == .None) testing.expectf(t, bits == 12, "jpeg_info reports %d bits, want 12", bits) img := jpeg_t_decode(t, JPEG_T_BITS12[:]) defer delete(img.data, context.allocator) testing.expectf(t, img.bits == 8, "decoded image claims %d bits", img.bits) testing.expect(t, len(img.data) == 64) for v, i in img.data { testing.expectf(t, v == 133, "sample %d is %d, want 133", i, v) } } // --------------------------------------------------------------------------- // Embedded files // --------------------------------------------------------------------------- @(private) JPEG_T_GREY8_PIXELS := [64]byte{ 20, 45, 71, 94, 121, 145, 170, 195, 22, 48, 73, 97, 124, 147, 173, 198, 26, 51, 77, 100, 127, 151, 176, 201, 29, 54, 80, 103, 130, 154, 179, 204, 31, 57, 82, 106, 133, 156, 182, 207, 34, 60, 85, 109, 135, 159, 185, 210, 38, 63, 89, 112, 139, 163, 188, 213, 40, 66, 91, 115, 141, 165, 191, 216, } @(private) JPEG_T_GREY8 := [341]byte{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0A, 0x07, 0x07, 0x06, 0x08, 0x0C, 0x0A, 0x0C, 0x0C, 0x0B, 0x0A, 0x0B, 0x0B, 0x0D, 0x0E, 0x12, 0x10, 0x0D, 0x0E, 0x11, 0x0E, 0x0B, 0x0B, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0C, 0x0F, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, 0x14, 0xFF, 0xC0, 0x00, 0x0B, 0x08, 0x00, 0x08, 0x00, 0x08, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3F, 0x00, 0xC4, 0xFD, 0x86, 0xF4, 0x4F, 0xF9, 0x07, 0xFC, 0xBF, 0xDD, 0xAF, 0xFF, 0xD9, } @(private) JPEG_T_GREY16 := [442]byte{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C, 0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B, 0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F, 0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E, 0xFF, 0xC0, 0x00, 0x0B, 0x08, 0x00, 0x10, 0x00, 0x10, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3F, 0x00, 0x4F, 0xF9, 0x3D, 0x1F, 0xFA, 0x91, 0x3F, 0xE1, 0x0E, 0xFF, 0x00, 0xB8, 0x97, 0xDA, 0xFE, 0xD9, 0xFF, 0x00, 0x7E, 0x7C, 0xBD, 0x9F, 0x65, 0xFF, 0x00, 0x6B, 0x3B, 0xFB, 0x63, 0x93, 0xFE, 0x4F, 0x47, 0xFE, 0xA4, 0x4F, 0xF8, 0x43, 0xBF, 0xEE, 0x25, 0xF6, 0xBF, 0xB6, 0x7F, 0xDF, 0x9F, 0x2F, 0x67, 0xD9, 0x7F, 0xDA, 0xCE, 0xFE, 0xD8, 0xE4, 0xFF, 0x00, 0x93, 0xD1, 0xFF, 0x00, 0xA9, 0x13, 0xFE, 0x10, 0xEF, 0xFB, 0x89, 0x7D, 0xAF, 0xED, 0x9F, 0xF7, 0xE7, 0xCB, 0xD9, 0xF6, 0x5F, 0xF6, 0xB3, 0xBF, 0xB6, 0x39, 0x3F, 0xE4, 0xF4, 0x7F, 0xEA, 0x44, 0xFF, 0x00, 0x84, 0x3B, 0xFE, 0xE2, 0x5F, 0x6B, 0xFB, 0x67, 0xFD, 0xF9, 0xF2, 0xF6, 0x7D, 0x97, 0xFD, 0xAC, 0xEF, 0xED, 0x8E, 0x7F, 0xFF, 0xD9, } @(private) JPEG_T_GREYPROG := [352]byte{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C, 0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B, 0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F, 0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E, 0xFF, 0xC2, 0x00, 0x0B, 0x08, 0x00, 0x10, 0x00, 0x10, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x15, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x83, 0xFF, 0xC4, 0x00, 0x15, 0x10, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x05, 0x02, 0xB4, 0x5A, 0x2D, 0x16, 0x8F, 0xFF, 0xC4, 0x00, 0x1B, 0x10, 0x00, 0x01, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x11, 0x14, 0x32, 0x54, 0x83, 0x91, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x06, 0x3F, 0x02, 0xC1, 0x87, 0xB0, 0x8F, 0x8D, 0x55, 0x83, 0x0F, 0x61, 0x1F, 0x1A, 0xAB, 0x06, 0x1E, 0xC2, 0x3E, 0x35, 0x56, 0x0C, 0x3D, 0x84, 0x7C, 0x6A, 0xAF, 0xFF, 0xC4, 0x00, 0x18, 0x10, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x71, 0xF0, 0xF1, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x3F, 0x21, 0xAB, 0x35, 0x36, 0x55, 0x9A, 0x9B, 0x2A, 0xCD, 0x4D, 0x95, 0x66, 0xA6, 0xCF, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x00, 0x10, 0xFF, 0x00, 0xFF, 0xC4, 0x00, 0x18, 0x10, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0xF0, 0x21, 0x51, 0x71, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x3F, 0x10, 0x7A, 0x8E, 0x95, 0x12, 0xF5, 0x1D, 0x2A, 0x25, 0xEA, 0x3A, 0x54, 0x4B, 0xD4, 0x74, 0xA8, 0x9F, 0xFF, 0xD9, } @(private) JPEG_T_RGB444 := [717]byte{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0A, 0x07, 0x07, 0x06, 0x08, 0x0C, 0x0A, 0x0C, 0x0C, 0x0B, 0x0A, 0x0B, 0x0B, 0x0D, 0x0E, 0x12, 0x10, 0x0D, 0x0E, 0x11, 0x0E, 0x0B, 0x0B, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0C, 0x0F, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, 0x14, 0xFF, 0xDB, 0x00, 0x43, 0x01, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x09, 0x05, 0x05, 0x09, 0x14, 0x0D, 0x0B, 0x0D, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x10, 0x00, 0x10, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3F, 0x00, 0xF9, 0x57, 0xC2, 0x9F, 0x0A, 0x7E, 0xE7, 0xEE, 0x7F, 0x4A, 0xFD, 0x83, 0x19, 0x9C, 0x6F, 0xA9, 0xE7, 0xF0, 0xDE, 0x7F, 0xF0, 0xFB, 0xC7, 0xB3, 0x78, 0x4F, 0xE1, 0x4F, 0xDC, 0xFD, 0xCF, 0xE9, 0x5F, 0x0D, 0x8C, 0xCE, 0x37, 0xD4, 0xFE, 0xAB, 0xE1, 0xBC, 0xFF, 0x00, 0xE1, 0xF7, 0x8F, 0x62, 0xF0, 0x9F, 0xC2, 0x9F, 0xB9, 0xFB, 0x9F, 0xD2, 0xBE, 0x1F, 0x19, 0x9C, 0x6F, 0xA9, 0xFE, 0x63, 0x70, 0xDE, 0x7D, 0xF0, 0xEA, 0x7B, 0x37, 0x85, 0x3E, 0x14, 0xFD, 0xCF, 0xDC, 0xFE, 0x95, 0xF0, 0xB8, 0xCC, 0xE3, 0x7D, 0x4F, 0xEA, 0xAE, 0x1B, 0xCF, 0xBE, 0x1D, 0x4F, 0xFF, 0xD9, } @(private) JPEG_T_RGB420 := [678]byte{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0A, 0x07, 0x07, 0x06, 0x08, 0x0C, 0x0A, 0x0C, 0x0C, 0x0B, 0x0A, 0x0B, 0x0B, 0x0D, 0x0E, 0x12, 0x10, 0x0D, 0x0E, 0x11, 0x0E, 0x0B, 0x0B, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0C, 0x0F, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, 0x14, 0xFF, 0xDB, 0x00, 0x43, 0x01, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x09, 0x05, 0x05, 0x09, 0x14, 0x0D, 0x0B, 0x0D, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x10, 0x00, 0x10, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3F, 0x00, 0xF9, 0x57, 0xC2, 0x9F, 0x0A, 0x7E, 0xE7, 0xEE, 0x7F, 0x4A, 0xF6, 0x6F, 0x09, 0xFC, 0x29, 0xFB, 0x9F, 0xB9, 0xFD, 0x2B, 0xD8, 0xBC, 0x27, 0xF0, 0xA7, 0xEE, 0x7E, 0xE7, 0xF4, 0xAF, 0x66, 0xF0, 0xA7, 0xC2, 0x9F, 0xB9, 0xFB, 0x9F, 0xD2, 0xBA, 0xF3, 0x9E, 0x30, 0xDF, 0xDE, 0x27, 0xC3, 0x6E, 0x3D, 0xF8, 0x3D, 0xFE, 0xC7, 0xFF, 0xD9, } @(private) JPEG_T_RGBPROG := [556]byte{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0A, 0x07, 0x07, 0x06, 0x08, 0x0C, 0x0A, 0x0C, 0x0C, 0x0B, 0x0A, 0x0B, 0x0B, 0x0D, 0x0E, 0x12, 0x10, 0x0D, 0x0E, 0x11, 0x0E, 0x0B, 0x0B, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0C, 0x0F, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, 0x14, 0xFF, 0xDB, 0x00, 0x43, 0x01, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x09, 0x05, 0x05, 0x09, 0x14, 0x0D, 0x0B, 0x0D, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xFF, 0xC2, 0x00, 0x11, 0x08, 0x00, 0x10, 0x00, 0x10, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xC4, 0x00, 0x15, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x07, 0xFF, 0xC4, 0x00, 0x15, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x10, 0x03, 0x10, 0x00, 0x00, 0x01, 0x95, 0x33, 0x62, 0xCE, 0xA7, 0xFF, 0xC4, 0x00, 0x16, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x05, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x05, 0x02, 0x56, 0x50, 0xA4, 0xA1, 0x49, 0x42, 0xB2, 0x8F, 0xFF, 0xC4, 0x00, 0x18, 0x11, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x21, 0x31, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x03, 0x01, 0x01, 0x3F, 0x01, 0x8D, 0xBE, 0xCB, 0x3F, 0xFF, 0xC4, 0x00, 0x17, 0x11, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x05, 0x61, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x02, 0x01, 0x01, 0x3F, 0x01, 0x72, 0xC6, 0x9F, 0xFF, 0xC4, 0x00, 0x15, 0x10, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x06, 0x3F, 0x02, 0x88, 0x88, 0xFF, 0xC4, 0x00, 0x15, 0x10, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x3F, 0x21, 0x9B, 0x16, 0x2C, 0xDF, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x0B, 0xFF, 0xC4, 0x00, 0x15, 0x11, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x03, 0x01, 0x01, 0x3F, 0x10, 0x9E, 0xFF, 0xC4, 0x00, 0x15, 0x11, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x02, 0x01, 0x01, 0x3F, 0x10, 0xB1, 0xFF, 0xC4, 0x00, 0x15, 0x10, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x3F, 0x10, 0x94, 0x84, 0x84, 0x94, 0xFF, 0xD9, } @(private) JPEG_T_CMYK := [431]byte{ 0xFF, 0xD8, 0xFF, 0xEE, 0x00, 0x0E, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0A, 0x07, 0x07, 0x06, 0x08, 0x0C, 0x0A, 0x0C, 0x0C, 0x0B, 0x0A, 0x0B, 0x0B, 0x0D, 0x0E, 0x12, 0x10, 0x0D, 0x0E, 0x11, 0x0E, 0x0B, 0x0B, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0C, 0x0F, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, 0x14, 0xFF, 0xC0, 0x00, 0x14, 0x08, 0x00, 0x10, 0x00, 0x10, 0x04, 0x43, 0x11, 0x00, 0x4D, 0x11, 0x00, 0x59, 0x11, 0x00, 0x4B, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x0E, 0x04, 0x43, 0x00, 0x4D, 0x00, 0x59, 0x00, 0x4B, 0x00, 0x00, 0x3F, 0x00, 0xFA, 0xF7, 0xF6, 0x92, 0xFF, 0x00, 0x97, 0xAF, 0xC6, 0xBE, 0xBD, 0xFF, 0x00, 0x86, 0x92, 0xFF, 0x00, 0xA7, 0xAF, 0xFC, 0x7A, 0xBE, 0x3C, 0xAF, 0xD0, 0x3A, 0xFC, 0xAA, 0xFD, 0xA4, 0xBF, 0xE5, 0xEB, 0xF1, 0xA3, 0xFE, 0x1A, 0x4B, 0xFE, 0x9E, 0xBF, 0xF1, 0xEA, 0x28, 0xAF, 0xD5, 0x5F, 0xDA, 0x4B, 0xFE, 0x5E, 0xBF, 0x1A, 0xFC, 0xAA, 0xFF, 0x00, 0x86, 0x92, 0xFF, 0x00, 0xA7, 0xAF, 0xFC, 0x7A, 0x8A, 0x2B, 0xF2, 0xAB, 0xF6, 0x92, 0xFF, 0x00, 0x97, 0xAF, 0xC6, 0x8F, 0xF8, 0x69, 0x2F, 0xFA, 0x7A, 0xFF, 0x00, 0xC7, 0xA8, 0xA2, 0xBF, 0xFF, 0xD9, } @(private) JPEG_T_RST := [410]byte{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C, 0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B, 0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F, 0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E, 0xFF, 0xC0, 0x00, 0x0B, 0x08, 0x00, 0x10, 0x00, 0x20, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x01, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3F, 0x00, 0xF0, 0x5D, 0x07, 0x4D, 0xFB, 0xBF, 0x2D, 0x7F, 0xFF, 0xD0, 0xE1, 0x74, 0x1D, 0x37, 0xEE, 0xFC, 0xB5, 0xFF, 0xD1, 0x9B, 0x41, 0xD3, 0x7E, 0xEF, 0xCB, 0x5F, 0xFF, 0xD2, 0xF4, 0x6D, 0x07, 0x4D, 0xFB, 0xBF, 0x2D, 0x7F, 0xFF, 0xD3, 0xF3, 0x8D, 0x07, 0x4D, 0xFB, 0xBF, 0x2D, 0x7F, 0xFF, 0xD4, 0x83, 0x41, 0xD3, 0x7E, 0xEF, 0xCB, 0x5F, 0xFF, 0xD5, 0xEE, 0x74, 0x1D, 0x37, 0xEE, 0xFC, 0xB5, 0xFF, 0xD6, 0xF7, 0x9D, 0x07, 0x4D, 0xFB, 0xBF, 0x2D, 0x7F, 0xFF, 0xD9, } @(private) JPEG_T_NORST := [388]byte{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C, 0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B, 0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F, 0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E, 0xFF, 0xC0, 0x00, 0x0B, 0x08, 0x00, 0x10, 0x00, 0x20, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3F, 0x00, 0xF0, 0x5D, 0x07, 0x4D, 0xFB, 0xBF, 0x2D, 0x7A, 0x06, 0x83, 0xA6, 0xFD, 0xDF, 0x96, 0xBD, 0x03, 0x41, 0xD3, 0x7E, 0xEF, 0xCB, 0x5E, 0x81, 0xA0, 0xE9, 0xBF, 0x77, 0xE5, 0xAF, 0x9C, 0xB4, 0x1D, 0x37, 0xEE, 0xFC, 0xB5, 0xE8, 0x1A, 0x0E, 0x9B, 0xF7, 0x7E, 0x5A, 0xF4, 0x0D, 0x07, 0x4D, 0xFB, 0xBF, 0x2D, 0x7A, 0x06, 0x83, 0xA6, 0xFD, 0xDF, 0x96, 0xBF, 0xFF, 0xD9, } @(private) JPEG_T_DCFLAT := [153]byte{ 0xFF, 0xD8, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xC0, 0x00, 0x0B, 0x08, 0x00, 0x08, 0x00, 0x08, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3F, 0x00, 0xEA, 0x1F, 0xFF, 0xD9, } @(private) JPEG_T_ODDSAMP := [177]byte{ 0xFF, 0xD8, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x08, 0x00, 0x20, 0x03, 0x01, 0x41, 0x00, 0x02, 0x11, 0x00, 0x03, 0x21, 0x00, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x3F, 0x00, 0xFE, 0x37, 0xDF, 0xD4, 0x03, 0xF5, 0x01, 0xFD, 0x40, 0x3E, 0x5F, 0x7D, 0xE0, 0xFC, 0x1F, 0x7F, 0xFF, 0xD9, } @(private) JPEG_T_PROGRST := [408]byte{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C, 0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B, 0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F, 0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E, 0xFF, 0xC2, 0x00, 0x0B, 0x08, 0x00, 0x10, 0x00, 0x10, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x01, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x3F, 0xFF, 0xD0, 0x3F, 0xFF, 0xD1, 0x3F, 0xFF, 0xD2, 0x3F, 0xFF, 0xC4, 0x00, 0x15, 0x10, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x05, 0x02, 0xB4, 0x7F, 0xFF, 0xD0, 0xB4, 0x7F, 0xFF, 0xD1, 0xB4, 0x7F, 0xFF, 0xD2, 0xB4, 0x7F, 0xFF, 0xC4, 0x00, 0x1B, 0x10, 0x00, 0x01, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x11, 0x14, 0x32, 0x54, 0x83, 0x91, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x06, 0x3F, 0x02, 0xC1, 0x87, 0xB0, 0x8F, 0x8D, 0x55, 0xFF, 0xD0, 0xC1, 0x87, 0xB0, 0x8F, 0x8D, 0x55, 0xFF, 0xD1, 0xC1, 0x87, 0xB0, 0x8F, 0x8D, 0x55, 0xFF, 0xD2, 0xC1, 0x87, 0xB0, 0x8F, 0x8D, 0x55, 0xFF, 0xC4, 0x00, 0x18, 0x10, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x71, 0xF0, 0xF1, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x3F, 0x21, 0xAB, 0x35, 0x36, 0x7F, 0xFF, 0xD0, 0xAB, 0x35, 0x36, 0x7F, 0xFF, 0xD1, 0xAB, 0x35, 0x36, 0x7F, 0xFF, 0xD2, 0xAB, 0x35, 0x36, 0x7F, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x00, 0x10, 0xFF, 0x00, 0xFF, 0xD0, 0xFF, 0x00, 0xFF, 0xD1, 0xFF, 0x00, 0xFF, 0xD2, 0xFF, 0x00, 0xFF, 0xC4, 0x00, 0x18, 0x10, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0xF0, 0x21, 0x51, 0x71, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x3F, 0x10, 0x7A, 0x8E, 0x95, 0x13, 0xFF, 0xD0, 0x7A, 0x8E, 0x95, 0x13, 0xFF, 0xD1, 0x7A, 0x8E, 0x95, 0x13, 0xFF, 0xD2, 0x7A, 0x8E, 0x95, 0x13, 0xFF, 0xD9, } @(private) JPEG_T_BITS12 := [154]byte{ 0xFF, 0xD8, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xC0, 0x00, 0x0B, 0x0C, 0x00, 0x08, 0x00, 0x08, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3F, 0x00, 0xFE, 0xA0, 0x1F, 0xFF, 0xD9, } // The bars image above encoded four ways: baseline and progressive, each with // and without restart markers. libjpeg-turbo at quality 90 over 64x64 pixels of // eight-row bars, which is what a page of text costs an encoder -- a handful of // bits per MCU. `-restart 3` and `-restart 2` are MCU rows, so the DRI segments // read 24 and 16. @(private) JPEG_T_LOWENT := [392]byte{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0A, 0x07, 0x07, 0x06, 0x08, 0x0C, 0x0A, 0x0C, 0x0C, 0x0B, 0x0A, 0x0B, 0x0B, 0x0D, 0x0E, 0x12, 0x10, 0x0D, 0x0E, 0x11, 0x0E, 0x0B, 0x0B, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0C, 0x0F, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, 0x14, 0xFF, 0xC0, 0x00, 0x0B, 0x08, 0x00, 0x40, 0x00, 0x40, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3F, 0x00, 0xFD, 0x10, 0xA2, 0x8A, 0x28, 0xA2, 0x8A, 0x2B, 0xF0, 0x12, 0x8A, 0x28, 0xA2, 0x8A, 0x28, 0xAF, 0xDF, 0xBA, 0x28, 0xA2, 0x8A, 0x28, 0xA2, 0xBF, 0x01, 0x28, 0xA2, 0x8A, 0x28, 0xA2, 0x8A, 0xFD, 0xFB, 0xA2, 0x8A, 0x28, 0xA2, 0x8A, 0x2B, 0xF0, 0x12, 0x8A, 0x28, 0xA2, 0x8A, 0x28, 0xAF, 0xDF, 0xBA, 0x28, 0xA2, 0x8A, 0x28, 0xA2, 0xBF, 0x01, 0x28, 0xA2, 0x8A, 0x28, 0xA2, 0x8A, 0xFF, 0xD9, } @(private) JPEG_T_LOWENT_RST := [403]byte{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0A, 0x07, 0x07, 0x06, 0x08, 0x0C, 0x0A, 0x0C, 0x0C, 0x0B, 0x0A, 0x0B, 0x0B, 0x0D, 0x0E, 0x12, 0x10, 0x0D, 0x0E, 0x11, 0x0E, 0x0B, 0x0B, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0C, 0x0F, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, 0x14, 0xFF, 0xC0, 0x00, 0x0B, 0x08, 0x00, 0x40, 0x00, 0x40, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x18, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3F, 0x00, 0xFD, 0x10, 0xA2, 0x8A, 0x28, 0xA2, 0x8A, 0x2B, 0xF0, 0x12, 0x8A, 0x28, 0xA2, 0x8A, 0x28, 0xAF, 0xDF, 0xBA, 0x28, 0xA2, 0x8A, 0x28, 0xA2, 0xBF, 0xFF, 0xD0, 0xF8, 0x52, 0x8A, 0x28, 0xA2, 0x8A, 0x28, 0xAF, 0xDF, 0xBA, 0x28, 0xA2, 0x8A, 0x28, 0xA2, 0xBF, 0x01, 0x28, 0xA2, 0x8A, 0x28, 0xA2, 0x8A, 0xFF, 0xD1, 0xFD, 0x10, 0xA2, 0x8A, 0x28, 0xA2, 0x8A, 0x2B, 0xF0, 0x12, 0x8A, 0x28, 0xA2, 0x8A, 0x28, 0xAF, 0xFF, 0xD9, } @(private) JPEG_T_LOWENTP := [308]byte{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0A, 0x07, 0x07, 0x06, 0x08, 0x0C, 0x0A, 0x0C, 0x0C, 0x0B, 0x0A, 0x0B, 0x0B, 0x0D, 0x0E, 0x12, 0x10, 0x0D, 0x0E, 0x11, 0x0E, 0x0B, 0x0B, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0C, 0x0F, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, 0x14, 0xFF, 0xC2, 0x00, 0x0B, 0x08, 0x00, 0x40, 0x00, 0x40, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x15, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0xA2, 0x00, 0x40, 0x20, 0x2F, 0xE0, 0x10, 0x08, 0x0B, 0xF8, 0x04, 0x02, 0x02, 0xFE, 0x01, 0x00, 0x80, 0xFF, 0xC4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x05, 0x02, 0x01, 0xFF, 0xC4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x06, 0x3F, 0x02, 0x01, 0xFF, 0xC4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x3F, 0x21, 0x01, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xC4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x3F, 0x10, 0x01, 0xFF, 0xD9, } @(private) JPEG_T_LOWENTP_RST := [365]byte{ 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0A, 0x07, 0x07, 0x06, 0x08, 0x0C, 0x0A, 0x0C, 0x0C, 0x0B, 0x0A, 0x0B, 0x0B, 0x0D, 0x0E, 0x12, 0x10, 0x0D, 0x0E, 0x11, 0x0E, 0x0B, 0x0B, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0C, 0x0F, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, 0x14, 0xFF, 0xC2, 0x00, 0x0B, 0x08, 0x00, 0x40, 0x00, 0x40, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00, 0x15, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x10, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0xA2, 0x00, 0x40, 0x20, 0x3F, 0xFF, 0xD0, 0xA2, 0x00, 0x40, 0x20, 0x3F, 0xFF, 0xD1, 0xA2, 0x00, 0x40, 0x20, 0x3F, 0xFF, 0xD2, 0xA2, 0x00, 0x40, 0x20, 0x3F, 0xFF, 0xC4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x05, 0x02, 0x07, 0xFF, 0xD0, 0x07, 0xFF, 0xD1, 0x07, 0xFF, 0xD2, 0x07, 0xFF, 0xC4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x06, 0x3F, 0x02, 0x07, 0xFF, 0xD0, 0x07, 0xFF, 0xD1, 0x07, 0xFF, 0xD2, 0x07, 0xFF, 0xC4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x3F, 0x21, 0x07, 0xFF, 0xD0, 0x07, 0xFF, 0xD1, 0x07, 0xFF, 0xD2, 0x07, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0xFF, 0x00, 0xFF, 0xD0, 0x00, 0xFF, 0x00, 0xFF, 0xD1, 0x00, 0xFF, 0x00, 0xFF, 0xD2, 0x00, 0xFF, 0x00, 0xFF, 0xC4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x3F, 0x10, 0x07, 0xFF, 0xD0, 0x07, 0xFF, 0xD1, 0x07, 0xFF, 0xD2, 0x07, 0xFF, 0xD9, }