mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Merge pull request #5668 from Kelimion/jpeg-updates
`core:image/jpeg` updates
This commit is contained in:
@@ -190,6 +190,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
options -= {.return_header}
|
options -= {.return_header}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if .do_not_expand_channels in options || .do_not_expand_grayscale in options {
|
||||||
|
return img, .Unsupported_Option
|
||||||
|
}
|
||||||
|
|
||||||
first := compress.read_u8(ctx) or_return
|
first := compress.read_u8(ctx) or_return
|
||||||
soi := cast(image.JPEG_Marker)compress.read_u8(ctx) or_return
|
soi := cast(image.JPEG_Marker)compress.read_u8(ctx) or_return
|
||||||
if first != 0xFF && soi != .SOI {
|
if first != 0xFF && soi != .SOI {
|
||||||
@@ -637,7 +641,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
color_components[id].h_sampling_factor = cast(int)horizontal_sampling
|
color_components[id].h_sampling_factor = cast(int)horizontal_sampling
|
||||||
}
|
}
|
||||||
case .SOF2: // Progressive DCT
|
case .SOF2: // Progressive DCT
|
||||||
unimplemented("SOF2")
|
fallthrough
|
||||||
case .SOF3: // Lossless (sequential)
|
case .SOF3: // Lossless (sequential)
|
||||||
fallthrough
|
fallthrough
|
||||||
case .SOF5: // Differential sequential DCT
|
case .SOF5: // Differential sequential DCT
|
||||||
@@ -927,9 +931,9 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
cbcr_pixel_column := k / luma_h_sampling_factor + 4 * h
|
cbcr_pixel_column := k / luma_h_sampling_factor + 4 * h
|
||||||
cbcr_pixel := cbcr_pixel_row * BLOCK_SIZE + cbcr_pixel_column
|
cbcr_pixel := cbcr_pixel_row * BLOCK_SIZE + cbcr_pixel_column
|
||||||
|
|
||||||
r := cast(i16)math.clamp(cast(f32)y_blk[.Y][i] + 1.402 * cast(f32)cbcr_blk[.Cr][cbcr_pixel] + 128, 0, 255)
|
r := cast(i16)clamp(cast(f32)y_blk[.Y][i] + 1.402 * cast(f32)cbcr_blk[.Cr][cbcr_pixel] + 128, 0, 255)
|
||||||
g := cast(i16)math.clamp(cast(f32)y_blk[.Y][i] - 0.344 * cast(f32)cbcr_blk[.Cb][cbcr_pixel] - 0.714 * cast(f32)cbcr_blk[.Cr][cbcr_pixel] + 128, 0, 255)
|
g := cast(i16)clamp(cast(f32)y_blk[.Y][i] - 0.344 * cast(f32)cbcr_blk[.Cb][cbcr_pixel] - 0.714 * cast(f32)cbcr_blk[.Cr][cbcr_pixel] + 128, 0, 255)
|
||||||
b := cast(i16)math.clamp(cast(f32)y_blk[.Y][i] + 1.772 * cast(f32)cbcr_blk[.Cb][cbcr_pixel] + 128, 0, 255)
|
b := cast(i16)clamp(cast(f32)y_blk[.Y][i] + 1.772 * cast(f32)cbcr_blk[.Cb][cbcr_pixel] + 128, 0, 255)
|
||||||
|
|
||||||
y_blk[.Y][i] = r
|
y_blk[.Y][i] = r
|
||||||
y_blk[.Cb][i] = g
|
y_blk[.Cb][i] = g
|
||||||
@@ -941,11 +945,26 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orig_channels := img.channels
|
||||||
|
|
||||||
|
// We automatically expand grayscale images to RGB
|
||||||
|
if img.channels == 1 {
|
||||||
|
img.channels += 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if .alpha_add_if_missing in options {
|
||||||
|
img.channels += 1
|
||||||
|
orig_channels += 1
|
||||||
|
}
|
||||||
|
|
||||||
if resize(&img.pixels.buf, img.width * img.height * img.channels) != nil {
|
if resize(&img.pixels.buf, img.width * img.height * img.channels) != nil {
|
||||||
return img, .Unable_To_Allocate_Or_Resize
|
return img, .Unable_To_Allocate_Or_Resize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch orig_channels {
|
||||||
|
case 1: // Grayscale JPEG expanded to RGB
|
||||||
out := mem.slice_data_cast([]image.RGB_Pixel, img.pixels.buf[:])
|
out := mem.slice_data_cast([]image.RGB_Pixel, img.pixels.buf[:])
|
||||||
|
out_idx := 0
|
||||||
for y in 0..<img.height {
|
for y in 0..<img.height {
|
||||||
mcu_row := y / BLOCK_SIZE
|
mcu_row := y / BLOCK_SIZE
|
||||||
pixel_row := y % BLOCK_SIZE
|
pixel_row := y % BLOCK_SIZE
|
||||||
@@ -955,19 +974,80 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
mcu_idx := mcu_row * block_width + mcu_col
|
mcu_idx := mcu_row * block_width + mcu_col
|
||||||
pixel_idx := pixel_row * BLOCK_SIZE + pixel_col
|
pixel_idx := pixel_row * BLOCK_SIZE + pixel_col
|
||||||
|
|
||||||
if img.channels == 3 {
|
luma := cast(byte)blocks[mcu_idx][.Y][pixel_idx]
|
||||||
out[y * img.width + x] = {
|
out[out_idx] = {luma, luma, luma}
|
||||||
|
|
||||||
|
out_idx += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2: // Grayscale JPEG expanded to RGBA
|
||||||
|
out := mem.slice_data_cast([]image.RGBA_Pixel, img.pixels.buf[:])
|
||||||
|
out_idx := 0
|
||||||
|
for y in 0..<img.height {
|
||||||
|
mcu_row := y / BLOCK_SIZE
|
||||||
|
pixel_row := y % BLOCK_SIZE
|
||||||
|
|
||||||
|
for x in 0..<img.width {
|
||||||
|
mcu_col := x / BLOCK_SIZE
|
||||||
|
pixel_col := x % BLOCK_SIZE
|
||||||
|
mcu_idx := mcu_row * block_width + mcu_col
|
||||||
|
pixel_idx := pixel_row * BLOCK_SIZE + pixel_col
|
||||||
|
|
||||||
|
luma := cast(byte)blocks[mcu_idx][.Y][pixel_idx]
|
||||||
|
out[out_idx] = {luma, luma, luma, 255}
|
||||||
|
out_idx += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
out := mem.slice_data_cast([]image.RGB_Pixel, img.pixels.buf[:])
|
||||||
|
out_idx := 0
|
||||||
|
for y in 0..<img.height {
|
||||||
|
mcu_row := y / BLOCK_SIZE
|
||||||
|
pixel_row := y % BLOCK_SIZE
|
||||||
|
|
||||||
|
for x in 0..<img.width {
|
||||||
|
mcu_col := x / BLOCK_SIZE
|
||||||
|
pixel_col := x % BLOCK_SIZE
|
||||||
|
mcu_idx := mcu_row * block_width + mcu_col
|
||||||
|
pixel_idx := pixel_row * BLOCK_SIZE + pixel_col
|
||||||
|
|
||||||
|
out[out_idx] = {
|
||||||
cast(byte)blocks[mcu_idx][.Y][pixel_idx],
|
cast(byte)blocks[mcu_idx][.Y][pixel_idx],
|
||||||
cast(byte)blocks[mcu_idx][.Cb][pixel_idx],
|
cast(byte)blocks[mcu_idx][.Cb][pixel_idx],
|
||||||
cast(byte)blocks[mcu_idx][.Cr][pixel_idx],
|
cast(byte)blocks[mcu_idx][.Cr][pixel_idx],
|
||||||
}
|
}
|
||||||
} else {
|
out_idx += 1
|
||||||
img.pixels.buf[y * img.width + x] = cast(byte)blocks[mcu_idx][.Y][pixel_idx]
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
out := mem.slice_data_cast([]image.RGBA_Pixel, img.pixels.buf[:])
|
||||||
|
out_idx := 0
|
||||||
|
for y in 0..<img.height {
|
||||||
|
mcu_row := y / BLOCK_SIZE
|
||||||
|
pixel_row := y % BLOCK_SIZE
|
||||||
|
|
||||||
|
for x in 0..<img.width {
|
||||||
|
mcu_col := x / BLOCK_SIZE
|
||||||
|
pixel_col := x % BLOCK_SIZE
|
||||||
|
mcu_idx := mcu_row * block_width + mcu_col
|
||||||
|
pixel_idx := pixel_row * BLOCK_SIZE + pixel_col
|
||||||
|
|
||||||
|
out[out_idx] = {
|
||||||
|
cast(byte)blocks[mcu_idx][.Y][pixel_idx],
|
||||||
|
cast(byte)blocks[mcu_idx][.Cb][pixel_idx],
|
||||||
|
cast(byte)blocks[mcu_idx][.Cr][pixel_idx],
|
||||||
|
255, // Alpha
|
||||||
|
}
|
||||||
|
out_idx += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expect_EOI = true
|
expect_EOI = true
|
||||||
|
|
||||||
case .TEM:
|
case .TEM:
|
||||||
// TEM doesn't have a length, continue to next marker
|
// TEM doesn't have a length, continue to next marker
|
||||||
case:
|
case:
|
||||||
|
|||||||
@@ -2,4 +2,6 @@
|
|||||||
*.zip
|
*.zip
|
||||||
*.png
|
*.png
|
||||||
*.jpg
|
*.jpg
|
||||||
|
*.qoi
|
||||||
|
*.pbm
|
||||||
math_big_test_library.*
|
math_big_test_library.*
|
||||||
@@ -281,6 +281,8 @@ HMAC_DIGESTS = {
|
|||||||
'shortfile.bmp': "be3ffade7999304f00f9b7d152b5b27811ad1166d0fd43004392467a28f44b6a4ec02a23c0296bacd4f02f8041cd824b9ca6c9fc31fed27e36e572113bb47d73",
|
'shortfile.bmp': "be3ffade7999304f00f9b7d152b5b27811ad1166d0fd43004392467a28f44b6a4ec02a23c0296bacd4f02f8041cd824b9ca6c9fc31fed27e36e572113bb47d73",
|
||||||
|
|
||||||
'emblem-1024.jpg': "d7b7e3ffaa5cda04c667e3742752091d78e02aa2d3c7a63406af679ce810a0a86666b10fcab12cc7ead2fadf2f6c2e1237bc94f892a62a4c218e18a20f96dbe4",
|
'emblem-1024.jpg': "d7b7e3ffaa5cda04c667e3742752091d78e02aa2d3c7a63406af679ce810a0a86666b10fcab12cc7ead2fadf2f6c2e1237bc94f892a62a4c218e18a20f96dbe4",
|
||||||
|
'emblem-1024-progressive.jpg': "7a6f4b112bd7189320c58dcddb9129968bcf268798c1e0c4f2243c10b3e3d9a6962c9f142d9fd65f8fb31e9a1e899008cae22b3ffde713250d315499b412e160",
|
||||||
|
'emblem-1024-gray.jpg': "4c25aaab92451e0452cdb165833b2b5a51978c2571de9d053950944667847666ba198d3001291615acda098ebe45b7d2d53c210c492f077b04a6bfe386f8a5fd",
|
||||||
|
|
||||||
'unicode.xml': "e0cdc94f07fdbb15eea811ed2ae6dcf494a83d197dafe6580c740270feb0d8f5f7146d4a7d4c2d2ea25f8bd9678bc986123484b39399819a6b7262687959d1ae",
|
'unicode.xml': "e0cdc94f07fdbb15eea811ed2ae6dcf494a83d197dafe6580c740270feb0d8f5f7146d4a7d4c2d2ea25f8bd9678bc986123484b39399819a6b7262687959d1ae",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
List of contributors:
|
List of contributors:
|
||||||
Jeroen van Rijn: Initial implementation.
|
Jeroen van Rijn: Initial implementation.
|
||||||
|
|
||||||
A test suite for PNG, TGA, NetPBM, QOI and BMP.
|
A test suite for PNG, TGA, NetPBM, QOI, BMP, and JPEG.
|
||||||
*/
|
*/
|
||||||
#+feature dynamic-literals
|
#+feature dynamic-literals
|
||||||
package test_core_image
|
package test_core_image
|
||||||
@@ -51,6 +51,7 @@ Blend_BG_Keep :: image.Options{.blend_background, .alpha_add_if_missing}
|
|||||||
Return_Metadata :: image.Options{.return_metadata}
|
Return_Metadata :: image.Options{.return_metadata}
|
||||||
No_Channel_Expansion :: image.Options{.do_not_expand_channels, .return_metadata}
|
No_Channel_Expansion :: image.Options{.do_not_expand_channels, .return_metadata}
|
||||||
|
|
||||||
|
|
||||||
Dims :: struct {
|
Dims :: struct {
|
||||||
width: int,
|
width: int,
|
||||||
height: int,
|
height: int,
|
||||||
@@ -2367,6 +2368,19 @@ Basic_JPG_Tests := []Test{
|
|||||||
{
|
{
|
||||||
"emblem-1024", {
|
"emblem-1024", {
|
||||||
{Default, nil, {1024, 1024, 3, 8}, 0x_46a29e0f},
|
{Default, nil, {1024, 1024, 3, 8}, 0x_46a29e0f},
|
||||||
|
{Alpha_Add, nil, {1024, 1024, 4, 8}, 0x_cae2d532},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"emblem-1024-progressive", {
|
||||||
|
{Default, .Unsupported_Frame_Type, {1024, 1024, 3, 8}, 0x_46a29e0f},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"emblem-1024-gray", {
|
||||||
|
{Default, nil, {1024, 1024, 3, 8}, 0x_4115d669},
|
||||||
|
{Alpha_Add, nil, {1024, 1024, 4, 8}, 0x_db496297},
|
||||||
|
{No_Channel_Expansion, .Unsupported_Option, {1024, 1024, 1, 8}, 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -2383,26 +2397,27 @@ run_jpg_suite :: proc(t: ^testing.T, suite: []Test) {
|
|||||||
|
|
||||||
for test in file.tests {
|
for test in file.tests {
|
||||||
img, err := jpeg.load(test_file, test.options)
|
img, err := jpeg.load(test_file, test.options)
|
||||||
|
defer jpeg.destroy(img)
|
||||||
|
|
||||||
passed := (test.expected_error == nil && err == nil) || (test.expected_error == err)
|
passed := (test.expected_error == nil && err == nil) || (test.expected_error == err)
|
||||||
testing.expectf(t, passed, "%q failed to load with error %v.", file.file, err)
|
testing.expectf(t, passed, "%q failed to load with error %v.", file.file, err)
|
||||||
|
|
||||||
if err == nil { // No point in running the other tests if it didn't load.
|
// No point in running the other tests if it didn't load.
|
||||||
pixels := bytes.buffer_to_bytes(&img.pixels)
|
(err == nil) or_continue
|
||||||
|
|
||||||
|
pixels := bytes.buffer_to_bytes(&img.pixels)
|
||||||
dims := Dims{img.width, img.height, img.channels, img.depth}
|
dims := Dims{img.width, img.height, img.channels, img.depth}
|
||||||
testing.expectf(t, test.dims == dims, "%v has %v, expected: %v.", file.file, dims, test.dims)
|
testing.expectf(t, test.dims == dims, "%v has %v, expected: %v.", file.file, dims, test.dims)
|
||||||
|
|
||||||
img_hash := hash.crc32(pixels)
|
img_hash := hash.crc32(pixels)
|
||||||
testing.expectf(t, test.hash == img_hash, "%v test #1's hash is %08x, expected %08x with %v.", file.file, img_hash, test.hash, test.options)
|
testing.expectf(t, test.hash == img_hash, "%v test #1's hash is %08x, expected %08x with %v.", file.file, img_hash, test.hash, test.options)
|
||||||
|
|
||||||
// Save to BMP file to check load
|
// Optionally save to QOI file to check file loaded properly during development
|
||||||
test_bmp := strings.concatenate({TEST_SUITE_PATH_JPG, "/", file.file, ".bmp"}, context.temp_allocator)
|
when false {
|
||||||
|
test_qoi := strings.concatenate({TEST_SUITE_PATH_JPG, "/", file.file, ".qoi"}, context.temp_allocator)
|
||||||
save_err := bmp.save(test_bmp, img)
|
save_err := qoi.save(test_qoi, img)
|
||||||
testing.expectf(t, save_err == nil, "expected saving to BMP in memory not to raise error, got %v", save_err)
|
testing.expectf(t, save_err == nil, "expected saving to QOI not to raise error, got %v", save_err)
|
||||||
}
|
}
|
||||||
bmp.destroy(img)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user