Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for reading jpeg gain maps and converting them to AVIF. #1565

Merged
merged 3 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
in the future. Files created now might not decode in a future version.
This feature is off by default and must be enabled with the
AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP compilation flag.
* Add experiment support for converting jpeg files with gain maps to AVIF
maryla-uc marked this conversation as resolved.
Show resolved Hide resolved
files with gain maps. Requires libxml2, and the AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP
compilation flag.
* Add the headerFormat member of new type avifHeaderFormat to avifEncoder.
* Add experimental API for reading and writing "avir"-branded AVIF files
behind the compilation flag AVIF_ENABLE_EXPERIMENTAL_AVIR.
Expand Down
39 changes: 39 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ if(AVIF_LOCAL_ZLIBPNG)

set(ZLIB_LIBRARY zlibstatic)
endif()

option(AVIF_LOCAL_JPEG "Build jpeg by providing your own copy inside the ext subdir." OFF)
if(AVIF_LOCAL_JPEG)
add_subdirectory(ext/libjpeg)
Expand All @@ -139,6 +140,7 @@ if(AVIF_LOCAL_JPEG)
set(JPEG_LIBRARY jpeg PARENT_SCOPE)
endif()
endif()

option(AVIF_LOCAL_LIBYUV "Build libyuv by providing your own copy inside the ext subdir." OFF)
if(AVIF_LOCAL_LIBYUV)
set(LIB_FILENAME "${CMAKE_CURRENT_SOURCE_DIR}/ext/libyuv/build/${AVIF_LIBRARY_PREFIX}yuv${AVIF_LIBRARY_SUFFIX}")
Expand Down Expand Up @@ -188,6 +190,7 @@ if(libyuv_FOUND)
set(AVIF_PLATFORM_INCLUDES ${AVIF_PLATFORM_INCLUDES} ${LIBYUV_INCLUDE_DIR})
set(AVIF_PLATFORM_LIBRARIES ${AVIF_PLATFORM_LIBRARIES} ${LIBYUV_LIBRARY})
endif(libyuv_FOUND)

option(AVIF_LOCAL_LIBSHARPYUV "Build libsharpyuv by providing your own copy inside the ext subdir." OFF)
if(AVIF_LOCAL_LIBSHARPYUV)
set(LIB_FILENAME "${CMAKE_CURRENT_SOURCE_DIR}/ext/libwebp/build/libsharpyuv${AVIF_LIBRARY_SUFFIX}")
Expand All @@ -213,6 +216,28 @@ if(libsharpyuv_FOUND)
else(libsharpyuv_FOUND)
message(STATUS "libavif: libsharpyuv not found")
endif(libsharpyuv_FOUND)

option(AVIF_LOCAL_LIBXML2 "Build libxml2 by providing your own copy inside the ext subdir. \
libxml2 is used when AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is ON" OFF
)
if(AVIF_LOCAL_LIBXML2)
set(LIB_FILENAME
"${CMAKE_CURRENT_SOURCE_DIR}/ext/libxml2/install.libxml2/lib/${AVIF_LIBRARY_PREFIX}xml2${AVIF_LIBRARY_SUFFIX}"
)
if(NOT EXISTS "${LIB_FILENAME}")
message(FATAL_ERROR "libavif: ${LIB_FILENAME} is missing, bailing out")
endif()
if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
set(LIBXML2_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ext/libxml2/install.libxml2/include/libxml2")
set(LIBXML2_LIBRARY ${LIB_FILENAME})
else()
set(LIBXML2_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ext/libxml2/install.libxml2/include/libxml2" PARENT_SCOPE)
set(LIBXML2_LIBRARY ${LIB_FILENAME} PARENT_SCOPE)
endif()
set(LIBXML2_FOUND TRUE)
else()
find_package(LibXml2 QUIET) # not required
endif()
# ---------------------------------------------------------------------------------------

# Enable all warnings
Expand Down Expand Up @@ -617,6 +642,17 @@ if(AVIF_BUILD_APPS OR (AVIF_BUILD_TESTS AND AVIF_ENABLE_GTEST))
avif_apps PRIVATE $<TARGET_PROPERTY:avif,INTERFACE_INCLUDE_DIRECTORIES> ${PNG_PNG_INCLUDE_DIR} ${JPEG_INCLUDE_DIR}
INTERFACE apps/shared
)

if(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
if(LIBXML2_FOUND)
set(AVIF_ENABLE_EXPERIMENTAL_JPEG_GAIN_MAP_CONVERSION TRUE)
add_compile_definitions(AVIF_ENABLE_EXPERIMENTAL_JPEG_GAIN_MAP_CONVERSION)
target_link_libraries(avif_apps ${LIBXML2_LIBRARY})
target_include_directories(avif_apps PRIVATE ${LIBXML2_INCLUDE_DIR})
else()
message(STATUS "libavif: libxml2 not found; avifenc will ignore any gain map in jpeg files")
endif()
endif()
endif()

if(AVIF_BUILD_APPS)
Expand Down Expand Up @@ -760,6 +796,9 @@ if(WIN32)
if(AVIF_LOCAL_JPEG)
avif_set_folder_safe(jpeg "ext/libjpeg")
endif()
if(AVIF_LOCAL_LIBXML2)
avif_set_folder_safe(xml2 "ext/libxml2")
endif()
endif()

add_subdirectory(contrib)
21 changes: 20 additions & 1 deletion apps/avifenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ typedef struct
avifBool ignoreExif;
avifBool ignoreXMP;
avifBool ignoreColorProfile;
avifBool ignoreGainMap; // only relevant when compiled with AVIF_ENABLE_EXPERIMENTAL_JPEG_GAIN_MAP_CONVERSION

// This holds the output timing for image sequences. The timescale member in this struct will
// become the timescale set on avifEncoder, and the duration member will be the default duration
Expand Down Expand Up @@ -209,6 +210,10 @@ static void syntaxLong(void)
printf(" --ignore-exif : If the input file contains embedded Exif metadata, ignore it (no-op if absent)\n");
printf(" --ignore-xmp : If the input file contains embedded XMP metadata, ignore it (no-op if absent)\n");
printf(" --ignore-profile,--ignore-icc : If the input file contains an embedded color profile, ignore it (no-op if absent)\n");
#if defined(AVIF_ENABLE_EXPERIMENTAL_JPEG_GAIN_MAP_CONVERSION)
printf(" --ignore-gain-map : If the input file contains an embedded gain map, ignore it (no-op if absent)\n");
// TODO(maryla): add quality setting for the gain map.
#endif
printf(" --pasp H,V : Add pasp property (aspect ratio). H=horizontal spacing, V=vertical spacing\n");
printf(" --crop CROPX,CROPY,CROPW,CROPH : Add clap property (clean aperture), but calculated from a crop rectangle\n");
printf(" --clap WN,WD,HN,HD,HON,HOD,VON,VOD: Add clap property (clean aperture). Width, Height, HOffset, VOffset (in num/denom pairs)\n");
Expand Down Expand Up @@ -482,6 +487,7 @@ static avifBool avifInputReadImage(avifInput * input,
avifBool ignoreExif,
avifBool ignoreXMP,
avifBool allowChangingCicp,
avifBool ignoreGainMap,
avifImage * image,
const avifInputFileSettings ** settings,
uint32_t * outDepth,
Expand Down Expand Up @@ -571,6 +577,7 @@ static avifBool avifInputReadImage(avifInput * input,
ignoreExif,
ignoreXMP,
allowChangingCicp,
ignoreGainMap,
dstImage,
dstDepth,
dstSourceTiming,
Expand Down Expand Up @@ -601,6 +608,7 @@ static avifBool avifInputReadImage(avifInput * input,
ignoreExif,
ignoreXMP,
allowChangingCicp,
ignoreGainMap,
image,
settings,
outDepth,
Expand Down Expand Up @@ -895,12 +903,14 @@ static avifBool avifEncodeRestOfImageSequence(avifEncoder * encoder,

// Ignore ICC, Exif and XMP because only the metadata of the first frame is taken into
// account by the libavif API.
// Ignore gain map as it's not supported for sequences.
if (!avifInputReadImage(input,
imageIndex,
/*ignoreColorProfile=*/AVIF_TRUE,
/*ignoreExif=*/AVIF_TRUE,
/*ignoreXMP=*/AVIF_TRUE,
/*allowChangingCicp=*/AVIF_FALSE,
/*ignoreGainMap=*/AVIF_TRUE,
nextImage,
&nextSettings,
/*outDepth=*/NULL,
Expand Down Expand Up @@ -1279,6 +1289,7 @@ int main(int argc, char * argv[])
settings.ignoreExif = AVIF_FALSE;
settings.ignoreXMP = AVIF_FALSE;
settings.ignoreColorProfile = AVIF_FALSE;
settings.ignoreGainMap = AVIF_FALSE;
settings.cicpExplicitlySet = AVIF_FALSE;

avifInputFileSettings pendingSettings;
Expand Down Expand Up @@ -1684,6 +1695,10 @@ int main(int argc, char * argv[])
settings.ignoreXMP = AVIF_TRUE;
} else if (!strcmp(arg, "--ignore-profile") || !strcmp(arg, "--ignore-icc")) {
settings.ignoreColorProfile = AVIF_TRUE;
#if defined(AVIF_ENABLE_EXPERIMENTAL_JPEG_GAIN_MAP_CONVERSION)
} else if (!strcmp(arg, "--ignore-gain-map")) {
settings.ignoreGainMap = AVIF_TRUE;
#endif
} else if (!strcmp(arg, "--pasp")) {
NEXTARG();
settings.paspCount = parseU32List(settings.paspValues, arg);
Expand Down Expand Up @@ -2031,12 +2046,16 @@ int main(int argc, char * argv[])
uint32_t sourceDepth = 0;
avifBool sourceWasRGB = AVIF_FALSE;
avifAppSourceTiming firstSourceTiming;
const avifBool isImageSequence = (settings.gridDimsCount == 0) && (input.filesCount > 1);
// Gain maps are not supported for animations or layered images.
const avifBool ignoreGainMap = settings.ignoreGainMap || isImageSequence || settings.progressive;
if (!avifInputReadImage(&input,
/*imageIndex=*/0,
settings.ignoreColorProfile,
settings.ignoreExif,
settings.ignoreXMP,
/*allowChangingCicp=*/!settings.cicpExplicitlySet,
ignoreGainMap,
image,
/*settings=*/NULL, // Must use the setting for first input
&sourceDepth,
Expand Down Expand Up @@ -2276,6 +2295,7 @@ int main(int argc, char * argv[])
/*ignoreExif=*/AVIF_TRUE,
/*ignoreXMP=*/AVIF_TRUE,
/*allowChangingCicp=*/AVIF_FALSE,
settings.ignoreGainMap,
y-guyon marked this conversation as resolved.
Show resolved Hide resolved
cellImage,
/*settings=*/NULL,
/*outDepth=*/NULL,
Expand Down Expand Up @@ -2327,7 +2347,6 @@ int main(int argc, char * argv[])
printf("Encoded successfully.\n");
printf(" * Color AV1 total size: %" AVIF_FMT_ZU " bytes\n", ioStats.colorOBUSize);
printf(" * Alpha AV1 total size: %" AVIF_FMT_ZU " bytes\n", ioStats.alphaOBUSize);
const avifBool isImageSequence = (settings.gridDimsCount == 0) && (input.filesCount > 1);
if (isImageSequence) {
if (settings.repetitionCount == AVIF_REPETITION_COUNT_INFINITE) {
printf(" * Repetition Count: Infinite\n");
Expand Down
Loading
Loading