diff --git a/AK/Debug.h.in b/AK/Debug.h.in index edd3e8d297b..e571002d47e 100644 --- a/AK/Debug.h.in +++ b/AK/Debug.h.in @@ -246,6 +246,10 @@ # cmakedefine01 JPEG_DEBUG #endif +#ifndef JPEG2000_DEBUG +# cmakedefine01 JPEG2000_DEBUG +#endif + #ifndef JS_BYTECODE_DEBUG # cmakedefine01 JS_BYTECODE_DEBUG #endif diff --git a/Base/res/apps/ImageViewer.af b/Base/res/apps/ImageViewer.af index e6ec0b1adff..8771a417635 100644 --- a/Base/res/apps/ImageViewer.af +++ b/Base/res/apps/ImageViewer.af @@ -4,4 +4,4 @@ Executable=/bin/ImageViewer Category=Gra&phics [Launcher] -FileTypes=bmp,dds,gif,ico,iff,jb2,jbig2,jpeg,jpg,jxl,lbm,pbm,pgm,png,ppm,qoi,tga,tiff,tif,tvg +FileTypes=bmp,dds,gif,ico,iff,jb2,jbig2,jp2,jpeg,jpg,jpx,jxl,lbm,pbm,pgm,png,ppm,qoi,tga,tiff,tif,tvg diff --git a/Meta/CMake/all_the_debug_macros.cmake b/Meta/CMake/all_the_debug_macros.cmake index b4465334046..d8e249ee453 100644 --- a/Meta/CMake/all_the_debug_macros.cmake +++ b/Meta/CMake/all_the_debug_macros.cmake @@ -92,6 +92,7 @@ set(ITEM_RECTS_DEBUG ON) set(JBIG2_DEBUG ON) set(JOB_DEBUG ON) set(JPEG_DEBUG ON) +set(JPEG2000_DEBUG ON) set(JS_BYTECODE_DEBUG ON) set(JS_MODULE_DEBUG ON) set(KEYBOARD_DEBUG ON) diff --git a/Meta/gn/secondary/AK/BUILD.gn b/Meta/gn/secondary/AK/BUILD.gn index dca87520c75..1c24d6168ff 100644 --- a/Meta/gn/secondary/AK/BUILD.gn +++ b/Meta/gn/secondary/AK/BUILD.gn @@ -289,6 +289,7 @@ write_cmake_config("ak_debug_gen") { "JOB_DEBUG=", "JBIG2_DEBUG=", "JPEG_DEBUG=", + "JPEG2000_DEBUG=", "JS_BYTECODE_DEBUG=", "JS_MODULE_DEBUG=", "KEYBOARD_SHORTCUTS_DEBUG=", diff --git a/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn index b20fcb50216..f94019cdce4 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn @@ -76,6 +76,7 @@ shared_library("LibGfx") { "ImageFormats/ISOBMFF/Reader.cpp", "ImageFormats/ImageDecoder.cpp", "ImageFormats/JBIG2Loader.cpp", + "ImageFormats/JPEG2000Loader.cpp", "ImageFormats/JPEGLoader.cpp", "ImageFormats/JPEGWriter.cpp", "ImageFormats/JPEGXLLoader.cpp", diff --git a/Userland/Libraries/LibCore/MimeData.cpp b/Userland/Libraries/LibCore/MimeData.cpp index 8398c72a9b5..a60efaccf2c 100644 --- a/Userland/Libraries/LibCore/MimeData.cpp +++ b/Userland/Libraries/LibCore/MimeData.cpp @@ -113,6 +113,7 @@ static Array const s_registered_mime_type = { MimeType { .name = "image/bmp"sv, .common_extensions = { ".bmp"sv }, .description = "BMP image data"sv, .magic_bytes = Vector { 'B', 'M' } }, MimeType { .name = "image/gif"sv, .common_extensions = { ".gif"sv }, .description = "GIF image data"sv, .magic_bytes = Vector { 'G', 'I', 'F', '8', '7', 'a' } }, MimeType { .name = "image/gif"sv, .common_extensions = { ".gif"sv }, .description = "GIF image data"sv, .magic_bytes = Vector { 'G', 'I', 'F', '8', '9', 'a' } }, + MimeType { .name = "image/jp2"sv, .common_extensions = { ".jp2"sv, ".jpx"sv }, .description = "JPEG2000 image data"sv, .magic_bytes = Vector { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A } }, MimeType { .name = "image/jpeg"sv, .common_extensions = { ".jpg"sv, ".jpeg"sv }, .description = "JPEG image data"sv, .magic_bytes = Vector { 0xFF, 0xD8, 0xFF } }, MimeType { .name = "image/jxl"sv, .common_extensions = { ".jxl"sv }, .description = "JPEG XL image data"sv, .magic_bytes = Vector { 0xFF, 0x0A } }, MimeType { .name = "image/png"sv, .common_extensions = { ".png"sv }, .description = "PNG image data"sv, .magic_bytes = Vector { 0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A } }, diff --git a/Userland/Libraries/LibGUI/FileTypeFilter.h b/Userland/Libraries/LibGUI/FileTypeFilter.h index 4097c304c6d..870dc5cef93 100644 --- a/Userland/Libraries/LibGUI/FileTypeFilter.h +++ b/Userland/Libraries/LibGUI/FileTypeFilter.h @@ -30,7 +30,7 @@ struct FileTypeFilter { static FileTypeFilter image_files() { - return FileTypeFilter { "Image Files", Vector { "png", "gif", "bmp", "dip", "pam", "pbm", "pgm", "ppm", "ico", "iff", "jb2", "jbig2", "jpeg", "jpg", "jxl", "dds", "qoi", "tif", "tiff", "webp", "tvg" } }; + return FileTypeFilter { "Image Files", Vector { "png", "gif", "bmp", "dip", "pam", "pbm", "pgm", "ppm", "ico", "iff", "jb2", "jbig2", "jp2", "jpeg", "jpg", "jpx", "jxl", "dds", "qoi", "tif", "tiff", "webp", "tvg" } }; } static FileTypeFilter video_files() diff --git a/Userland/Libraries/LibGfx/Bitmap.h b/Userland/Libraries/LibGfx/Bitmap.h index 3650b68831b..bd050efc9d5 100644 --- a/Userland/Libraries/LibGfx/Bitmap.h +++ b/Userland/Libraries/LibGfx/Bitmap.h @@ -16,28 +16,30 @@ #include #include -#define ENUMERATE_IMAGE_FORMATS \ - __ENUMERATE_IMAGE_FORMAT(bmp, ".bmp") \ - __ENUMERATE_IMAGE_FORMAT(dds, ".dds") \ - __ENUMERATE_IMAGE_FORMAT(gif, ".gif") \ - __ENUMERATE_IMAGE_FORMAT(ico, ".ico") \ - __ENUMERATE_IMAGE_FORMAT(iff, ".iff") \ - __ENUMERATE_IMAGE_FORMAT(jpeg, ".jb2") \ - __ENUMERATE_IMAGE_FORMAT(jpeg, ".jbig2") \ - __ENUMERATE_IMAGE_FORMAT(jpeg, ".jpeg") \ - __ENUMERATE_IMAGE_FORMAT(jpeg, ".jpg") \ - __ENUMERATE_IMAGE_FORMAT(jxl, ".jxl") \ - __ENUMERATE_IMAGE_FORMAT(iff, ".lbm") \ - __ENUMERATE_IMAGE_FORMAT(pam, ".pam") \ - __ENUMERATE_IMAGE_FORMAT(pbm, ".pbm") \ - __ENUMERATE_IMAGE_FORMAT(pgm, ".pgm") \ - __ENUMERATE_IMAGE_FORMAT(png, ".png") \ - __ENUMERATE_IMAGE_FORMAT(ppm, ".ppm") \ - __ENUMERATE_IMAGE_FORMAT(qoi, ".qoi") \ - __ENUMERATE_IMAGE_FORMAT(tga, ".tga") \ - __ENUMERATE_IMAGE_FORMAT(tiff, ".tif") \ - __ENUMERATE_IMAGE_FORMAT(tiff, ".tiff") \ - __ENUMERATE_IMAGE_FORMAT(tvg, ".tvg") \ +#define ENUMERATE_IMAGE_FORMATS \ + __ENUMERATE_IMAGE_FORMAT(bmp, ".bmp") \ + __ENUMERATE_IMAGE_FORMAT(dds, ".dds") \ + __ENUMERATE_IMAGE_FORMAT(gif, ".gif") \ + __ENUMERATE_IMAGE_FORMAT(ico, ".ico") \ + __ENUMERATE_IMAGE_FORMAT(iff, ".iff") \ + __ENUMERATE_IMAGE_FORMAT(jpeg, ".jb2") \ + __ENUMERATE_IMAGE_FORMAT(jpeg, ".jbig2") \ + __ENUMERATE_IMAGE_FORMAT(jpeg2000, ".jp2") \ + __ENUMERATE_IMAGE_FORMAT(jpeg, ".jpeg") \ + __ENUMERATE_IMAGE_FORMAT(jpeg, ".jpg") \ + __ENUMERATE_IMAGE_FORMAT(jpeg2000, ".jpx") \ + __ENUMERATE_IMAGE_FORMAT(jxl, ".jxl") \ + __ENUMERATE_IMAGE_FORMAT(iff, ".lbm") \ + __ENUMERATE_IMAGE_FORMAT(pam, ".pam") \ + __ENUMERATE_IMAGE_FORMAT(pbm, ".pbm") \ + __ENUMERATE_IMAGE_FORMAT(pgm, ".pgm") \ + __ENUMERATE_IMAGE_FORMAT(png, ".png") \ + __ENUMERATE_IMAGE_FORMAT(ppm, ".ppm") \ + __ENUMERATE_IMAGE_FORMAT(qoi, ".qoi") \ + __ENUMERATE_IMAGE_FORMAT(tga, ".tga") \ + __ENUMERATE_IMAGE_FORMAT(tiff, ".tif") \ + __ENUMERATE_IMAGE_FORMAT(tiff, ".tiff") \ + __ENUMERATE_IMAGE_FORMAT(tvg, ".tvg") \ __ENUMERATE_IMAGE_FORMAT(tvg, ".webp") namespace Gfx { diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index 70228efbc6b..54abc6c2746 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -49,6 +49,7 @@ set(SOURCES ImageFormats/ISOBMFF/JPEG2000Boxes.cpp ImageFormats/ISOBMFF/Reader.cpp ImageFormats/JBIG2Loader.cpp + ImageFormats/JPEG2000Loader.cpp ImageFormats/JPEGLoader.cpp ImageFormats/JPEGXLLoader.cpp ImageFormats/JPEGWriter.cpp diff --git a/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp b/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp index de0977ce37b..8a24bdafa38 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ static ErrorOr> probe_and_sniff_for_appropriate_plugi { ICOImageDecoderPlugin::sniff, ICOImageDecoderPlugin::create }, { ILBMImageDecoderPlugin::sniff, ILBMImageDecoderPlugin::create }, { JBIG2ImageDecoderPlugin::sniff, JBIG2ImageDecoderPlugin::create }, + { JPEG2000ImageDecoderPlugin::sniff, JPEG2000ImageDecoderPlugin::create }, { JPEGImageDecoderPlugin::sniff, JPEGImageDecoderPlugin::create }, { JPEGXLImageDecoderPlugin::sniff, JPEGXLImageDecoderPlugin::create }, { PAMImageDecoderPlugin::sniff, PAMImageDecoderPlugin::create }, diff --git a/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp b/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp new file mode 100644 index 00000000000..3e78ca3de3a --- /dev/null +++ b/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +// Core coding system spec (.jp2 format): T-REC-T.800-201511-S!!PDF-E.pdf available here: +// https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.800-201511-S!!PDF-E&type=items + +// Extensions (.jpx format): T-REC-T.801-202106-S!!PDF-E.pdf available here: +// https://handle.itu.int/11.1002/1000/14666-en?locatt=format:pdf&auth + +// rfc3745 lists the MIME type. It only mentions the jp2_id_string as magic number. + +namespace Gfx { + +// A JPEG2000 image can be stored in a codestream with markers, similar to a JPEG image, +// or in a JP2 file, which is a container format based on boxes similar to ISOBMFF. + +// This is the marker for the codestream version. We don't support this yet. +// If we add support, add a second `"image/jp2"` line to MimeData.cpp for this magic number. +// T.800 Annex A, Codestream syntax, A.2 Information in the marker segments and A.3 Construction of the codestream +[[maybe_unused]] static constexpr u8 marker_id_string[] = { 0xFF, 0x4F, 0xFF, 0x51 }; + +// This is the marker for the box version. +// T.800 Annex I, JP2 file format syntax, I.5.1 JPEG 2000 Signature box +static constexpr u8 jp2_id_string[] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A }; + +bool JPEG2000ImageDecoderPlugin::sniff(ReadonlyBytes data) +{ + return data.starts_with(jp2_id_string); +} + +ErrorOr> JPEG2000ImageDecoderPlugin::create(ReadonlyBytes) +{ + return Error::from_string_view("FIXME: Draw the rest of the owl"sv); +} + +} diff --git a/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.h b/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.h new file mode 100644 index 00000000000..1066e8ee585 --- /dev/null +++ b/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Gfx { + +class JPEG2000ImageDecoderPlugin : public ImageDecoderPlugin { +public: + static bool sniff(ReadonlyBytes); + static ErrorOr> create(ReadonlyBytes); + + virtual ~JPEG2000ImageDecoderPlugin() override = default; +}; + +}