48 changes: 44 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.9.2] - 2021-06-23

### Added

* avifenc, avifdec: Allow "-j all" to automatically use all of the cores on the machine (#670)

### Changed

* Refactor imir implementation to match HEIF Draft Amendment 2 (#665)
* Merge avifCodec's open call with its getNextImage call to avoid codec init during parse, and simplify the codec API (#637)
* Update aom.cmd: v3.1.1 (#674)
* Update svt-av1: v0.8.7 (#627)
* Make tests/compare.h and tests/testcase.h C++ safe (#678)
* Print width and height as %ux%u instead of %u/%u (#676)
* Allocate codec->internal->svt_config statically (#675)
* Cleanup related to avifDiagnosticsClearError() (#673)
* Cleanup avifutil.h comment to match libavif style (#671)
* Fix the clang -Wunused-macros warning (#672)
* Check for int32_t overflows in 'clap' code (#663)
* Have avifdec print chroma sample position for 420 (#666)
* Enable CMake configs in VCPKG mode (#659)
* Avoid multiplying widthN and heightN by 2 (#662)
* Correct AVIF_PIXEL_FORMAT_NONE handling logic (#654)
* Cast extent->offset (a uint64_t) to size_t safely (#660)
* Disallow negative clap width or height (#656)
* Check for int32_t cast and unsigned add overflows (#655)
* Some straightforward changes to clapFraction code (#653)
* Fix box name of avifParseChunkOffsetBox (#652)
* No need to pass diag to functions that have 'data' (#651)
* Simplify the assertion in avifROStreamStart() (#650)
* Don't clear error in avifEncoderSetCodecSpecificOp (#648)
* Simplify avifCodecConfigurationBoxGetFormat (#646)
* Print the fraction in "not an integer" messages (#641)
* Fix a typo in the diagnostic context for 'ipco' (#644)
* Remove const from non-pointer function parameters (#634)
* Declare the param of avifDumpDiagnostics as const (#633)
* Adjust gdk-pixbuf loader for new API change (#668)
* Fix gdk-pixbuf loader install path (#615)
* Don't need to disable MSVC warnings 5031 and 5032 (#681)

## [0.9.1] - 2021-05-19

### Added
Expand All @@ -17,7 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* avifenc: New default for `--yuv`: `"auto"`, which will use a source JPEG's internal YUV format instead of YUV444, if detected
* Uses: Prevent colorspace conversion when reading from JPEG if possible (tongyuantongyu)
* avifenc/avifdec: Add helpful values/calculations when dumping clap box
* Added avifDiagnostics, which allows for a detailed, freeform error string upon decode error
* Added avifDiagnostics, which allows for a detailed, freeform error string upon decode or encode error
* Create helper avifCropRect struct and methods for helping to manipulate/populate/validate avifCleanApertureBox
* Added ability to set codec-specific options for color or alpha only
* Support for libaom's ALL_INTRA mode (if available)
Expand Down Expand Up @@ -45,8 +85,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Tweaks to compiler flags (analyze related)
* Use libyuv BT.709 & 2020 full range YuvConstants (wantehchang)
* Multiply color with alpha for opaque RGB format during conversion (see #520)
* switch docker to ubuntu 20.04, fix tzdata install (paskal)

* Switch docker to ubuntu 20.04, fix tzdata install (paskal)
* Added an "Understanding maxThreads" explanatory comment block in avif.h
* Minor fixes to support AVIF_CODEC_AOM_ENCODE
* Various minor code/comments cleanup
Expand Down Expand Up @@ -625,7 +664,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Constants `AVIF_VERSION`, `AVIF_VERSION_MAJOR`, `AVIF_VERSION_MINOR`, `AVIF_VERSION_PATCH`
- `avifVersion()` function

[Unreleased]: https://github.com/AOMediaCodec/libavif/compare/v0.9.1...HEAD
[Unreleased]: https://github.com/AOMediaCodec/libavif/compare/v0.9.2...HEAD
[0.9.2]: https://github.com/AOMediaCodec/libavif/compare/v0.9.1...v0.9.2
[0.9.1]: https://github.com/AOMediaCodec/libavif/compare/v0.9.0...v0.9.1
[0.9.0]: https://github.com/AOMediaCodec/libavif/compare/v0.8.4...v0.9.0
[0.8.4]: https://github.com/AOMediaCodec/libavif/compare/v0.8.3...v0.8.4
Expand Down
5 changes: 3 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ set(CMAKE_C_STANDARD 99)
# Increment MINOR. Set PATCH to 0
# If the source code was changed, but there were no interface changes:
# Increment PATCH.
set(LIBRARY_VERSION_MAJOR 11)
set(LIBRARY_VERSION_MAJOR 12)
set(LIBRARY_VERSION_MINOR 0)
set(LIBRARY_VERSION_PATCH 0)
set(LIBRARY_VERSION "${LIBRARY_VERSION_MAJOR}.${LIBRARY_VERSION_MINOR}.${LIBRARY_VERSION_PATCH}")
Expand Down Expand Up @@ -564,7 +564,8 @@ if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL)
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
)

if (BUILD_SHARED_LIBS)
# Enable CMake configs in VCPKG mode
if (BUILD_SHARED_LIBS OR VCPKG_TARGET_TRIPLET)
install(EXPORT ${PROJECT_NAME}-config
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})

Expand Down
12 changes: 8 additions & 4 deletions apps/avifdec.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ static void syntax(void)
printf("Options:\n");
printf(" -h,--help : Show syntax help\n");
printf(" -V,--version : Show the version number\n");
printf(" -j,--jobs J : Number of jobs (worker threads, default: 1)\n");
printf(" -j,--jobs J : Number of jobs (worker threads, default: 1. Use \"all\" to use all available cores)\n");
printf(" -c,--codec C : AV1 codec to use (choose from versions list below)\n");
printf(" -d,--depth D : Output depth [8,16]. (PNG only; For y4m, depth is retained, and JPEG is always 8bpc)\n");
printf(" -q,--quality Q : Output quality [0-100]. (JPEG only, default: %d)\n", DEFAULT_JPEG_QUALITY);
Expand Down Expand Up @@ -74,9 +74,13 @@ int main(int argc, char * argv[])
return 0;
} else if (!strcmp(arg, "-j") || !strcmp(arg, "--jobs")) {
NEXTARG();
jobs = atoi(arg);
if (jobs < 1) {
jobs = 1;
if (!strcmp(arg, "all")) {
jobs = avifQueryCPUCount();
} else {
jobs = atoi(arg);
if (jobs < 1) {
jobs = 1;
}
}
} else if (!strcmp(arg, "-c") || !strcmp(arg, "--codec")) {
NEXTARG();
Expand Down
34 changes: 18 additions & 16 deletions apps/avifenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ static void syntax(void)
printf("Options:\n");
printf(" -h,--help : Show syntax help\n");
printf(" -V,--version : Show the version number\n");
printf(" -j,--jobs J : Number of jobs (worker threads, default: 1)\n");
printf(" -j,--jobs J : Number of jobs (worker threads, default: 1. Use \"all\" to use all available cores)\n");
printf(" -o,--output FILENAME : Instead of using the last filename given as output, use this filename\n");
printf(" -l,--lossless : Set all defaults to encode losslessly, and emit warnings when settings/input don't allow for it\n");
printf(" -d,--depth D : Output depth [8,10,12]. (JPEG/PNG only; For y4m or stdin, depth is retained)\n");
Expand Down Expand Up @@ -103,7 +103,7 @@ static void syntax(void)
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");
printf(" --irot ANGLE : Add irot property (rotation). [0-3], makes (90 * ANGLE) degree rotation anti-clockwise\n");
printf(" --imir AXIS : Add imir property (mirroring). 0=vertical axis (\"left-to-right\"), 1=horizontal axis (\"top-to-bottom\")\n");
printf(" --imir MODE : Add imir property (mirroring). 0=top-to-bottom, 1=left-to-right\n");
printf("\n");
if (avifCodecName(AVIF_CODEC_CHOICE_AOM, 0)) {
printf("aom-specific advanced options:\n");
Expand Down Expand Up @@ -439,7 +439,7 @@ int main(int argc, char * argv[])
uint32_t clapValues[8];
avifBool cropConversionRequired = AVIF_FALSE;
uint8_t irotAngle = 0xff; // sentinel value indicating "unused"
uint8_t imirAxis = 0xff; // sentinel value indicating "unused"
uint8_t imirMode = 0xff; // sentinel value indicating "unused"
avifCodecChoice codecChoice = AVIF_CODEC_CHOICE_AUTO;
avifRange requestedRange = AVIF_RANGE_FULL;
avifBool lossless = AVIF_FALSE;
Expand Down Expand Up @@ -487,9 +487,13 @@ int main(int argc, char * argv[])
goto cleanup;
} else if (!strcmp(arg, "-j") || !strcmp(arg, "--jobs")) {
NEXTARG();
jobs = atoi(arg);
if (jobs < 1) {
jobs = 1;
if (!strcmp(arg, "all")) {
jobs = avifQueryCPUCount();
} else {
jobs = atoi(arg);
if (jobs < 1) {
jobs = 1;
}
}
} else if (!strcmp(arg, "--stdin")) {
input.useStdin = AVIF_TRUE;
Expand Down Expand Up @@ -729,9 +733,9 @@ int main(int argc, char * argv[])
}
} else if (!strcmp(arg, "--imir")) {
NEXTARG();
imirAxis = (uint8_t)atoi(arg);
if (imirAxis > 1) {
fprintf(stderr, "ERROR: Invalid imir axis: %s\n", arg);
imirMode = (uint8_t)atoi(arg);
if (imirMode > 1) {
fprintf(stderr, "ERROR: Invalid imir mode: %s\n", arg);
returnCode = 1;
goto cleanup;
}
Expand Down Expand Up @@ -793,11 +797,9 @@ int main(int argc, char * argv[])

if ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) && (input.requestedFormat != AVIF_PIXEL_FORMAT_NONE) &&
(input.requestedFormat != AVIF_PIXEL_FORMAT_YUV444)) {
// This protects against this code misbehaving if AVIF_APP_DEFAULT_PIXEL_FORMAT is ever changed from AVIF_PIXEL_FORMAT_YUV444
assert((input.requestedFormat != AVIF_PIXEL_FORMAT_NONE) || (AVIF_APP_DEFAULT_PIXEL_FORMAT == AVIF_PIXEL_FORMAT_YUV444));

// matrixCoefficients was likely set to AVIF_MATRIX_COEFFICIENTS_IDENTITY as a side effect
// of --lossless, and Identity is only valid with YUV444. Set this back to the default.
// User explicitly asked for non YUV444 yuvFormat, while matrixCoefficients was likely
// set to AVIF_MATRIX_COEFFICIENTS_IDENTITY as a side effect of --lossless,
// and Identity is only valid with YUV444. Set matrixCoefficients back to the default.
image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601;

if (cicpExplicitlySet) {
Expand Down Expand Up @@ -911,9 +913,9 @@ int main(int argc, char * argv[])
image->transformFlags |= AVIF_TRANSFORM_IROT;
image->irot.angle = irotAngle;
}
if (imirAxis != 0xff) {
if (imirMode != 0xff) {
image->transformFlags |= AVIF_TRANSFORM_IMIR;
image->imir.axis = imirAxis;
image->imir.mode = imirMode;
}

avifBool usingAOM = AVIF_FALSE;
Expand Down
5 changes: 4 additions & 1 deletion apps/shared/avifjpeg.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,10 @@ avifBool avifJPEGRead(const char * inputFilename, avifImage * avif, avifPixelFor

avif->width = cinfo.output_width;
avif->height = cinfo.output_height;
avif->yuvFormat = (requestedFormat == AVIF_PIXEL_FORMAT_NONE) ? AVIF_APP_DEFAULT_PIXEL_FORMAT : requestedFormat;
if (avif->yuvFormat == AVIF_PIXEL_FORMAT_NONE) {
// Identity is only valid with YUV444.
avif->yuvFormat = (avif->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) ? AVIF_PIXEL_FORMAT_YUV444 : AVIF_APP_DEFAULT_PIXEL_FORMAT;
}
avif->depth = requestedDepth ? requestedDepth : 8;
avifRGBImageSetDefaults(&rgb, avif);
rgb.format = AVIF_RGB_FORMAT_RGB;
Expand Down
6 changes: 5 additions & 1 deletion apps/shared/avifpng.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,11 @@ avifBool avifPNGRead(const char * inputFilename, avifImage * avif, avifPixelForm

avif->width = rawWidth;
avif->height = rawHeight;
avif->yuvFormat = (requestedFormat == AVIF_PIXEL_FORMAT_NONE) ? AVIF_APP_DEFAULT_PIXEL_FORMAT : requestedFormat;
avif->yuvFormat = requestedFormat;
if (avif->yuvFormat == AVIF_PIXEL_FORMAT_NONE) {
// Identity is only valid with YUV444.
avif->yuvFormat = (avif->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) ? AVIF_PIXEL_FORMAT_YUV444 : AVIF_APP_DEFAULT_PIXEL_FORMAT;
}
avif->depth = requestedDepth;
if (avif->depth == 0) {
if (imgBitDepth == 8) {
Expand Down
80 changes: 75 additions & 5 deletions apps/shared/avifutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ static void printClapFraction(const char * name, int32_t n, int32_t d)
printf(", ");
}

static void avifImageDumpInternal(avifImage * avif, uint32_t gridCols, uint32_t gridRows, avifBool alphaPresent)
static void avifImageDumpInternal(const avifImage * avif, uint32_t gridCols, uint32_t gridRows, avifBool alphaPresent)
{
uint32_t width = avif->width;
uint32_t height = avif->height;
Expand All @@ -49,6 +49,9 @@ static void avifImageDumpInternal(avifImage * avif, uint32_t gridCols, uint32_t
printf(" * Resolution : %ux%u\n", width, height);
printf(" * Bit Depth : %u\n", avif->depth);
printf(" * Format : %s\n", avifPixelFormatToString(avif->yuvFormat));
if (avif->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
printf(" * Chroma Sam. Pos: %u\n", avif->yuvChromaSamplePosition);
}
printf(" * Alpha : %s\n", alphaPresent ? (avif->alphaPremultiplied ? "Premultiplied" : "Not premultiplied") : "Absent");
if (avif->alphaRange == AVIF_RANGE_LIMITED) {
printf(" Limited range\n");
Expand Down Expand Up @@ -99,9 +102,7 @@ static void avifImageDumpInternal(avifImage * avif, uint32_t gridCols, uint32_t
printf(" * irot (Rotation) : %u\n", avif->irot.angle);
}
if (avif->transformFlags & AVIF_TRANSFORM_IMIR) {
printf(" * imir (Mirror) : %u (%s)\n",
avif->imir.axis,
(avif->imir.axis == 0) ? "Vertical axis, \"left-to-right\"" : "Horizontal axis, \"top-to-bottom\"");
printf(" * imir (Mirror) : Mode %u (%s)\n", avif->imir.mode, (avif->imir.mode == 0) ? "top-to-bottom" : "left-to-right");
}
}
}
Expand Down Expand Up @@ -210,7 +211,7 @@ avifAppFileFormat avifGuessFileFormat(const char * filename)
return AVIF_APP_FILE_FORMAT_UNKNOWN;
}

void avifDumpDiagnostics(struct avifDiagnostics * diag)
void avifDumpDiagnostics(const avifDiagnostics * diag)
{
if (!*diag->error) {
return;
Expand All @@ -219,3 +220,72 @@ void avifDumpDiagnostics(struct avifDiagnostics * diag)
printf("Diagnostics:\n");
printf(" * %s\n", diag->error);
}

// ---------------------------------------------------------------------------
// avifQueryCPUCount (separated into OS implementations)

#if defined(_WIN32)

// Windows

#include <windows.h>

int avifQueryCPUCount(void)
{
int numCPU;
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
numCPU = sysinfo.dwNumberOfProcessors;
return numCPU;
}

#elif defined(__APPLE__)

// Apple

#include <sys/sysctl.h>

int avifQueryCPUCount()
{
int mib[4];
int numCPU;
size_t len = sizeof(numCPU);

/* set the mib for hw.ncpu */
mib[0] = CTL_HW;
mib[1] = HW_AVAILCPU; // alternatively, try HW_NCPU;

/* get the number of CPUs from the system */
sysctl(mib, 2, &numCPU, &len, NULL, 0);

if (numCPU < 1) {
mib[1] = HW_NCPU;
sysctl(mib, 2, &numCPU, &len, NULL, 0);
if (numCPU < 1)
numCPU = 1;
}
return numCPU;
}

#elif defined(__EMSCRIPTEN__)

// Emscripten

int avifQueryCPUCount()
{
return 1;
}

#else

// POSIX

#include <unistd.h>

int avifQueryCPUCount()
{
int numCPU = (int)sysconf(_SC_NPROCESSORS_ONLN);
return (numCPU > 0) ? numCPU : 1;
}

#endif
18 changes: 9 additions & 9 deletions apps/shared/avifutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@

#include "avif/avif.h"

/*
* The %z format specifier is not available with Visual Studios before 2013 and
* mingw-w64 toolchains with `__USE_MINGW_ANSI_STDIO` not set to 1.
* Hence the %I format specifier must be used instead to print out `size_t`.
* Newer Visual Studios and mingw-w64 toolchains built with the commit
* mentioned with c99 set as the standard supports the %z specifier properly.
* Related mingw-w64 commit: bfd33f6c0ec5e652cc9911857dd1492ece8d8383
*/
// The %z format specifier is not available with Visual Studios before 2013 and mingw-w64 toolchains
// with `__USE_MINGW_ANSI_STDIO` not set to 1. Hence the %I format specifier must be used instead
// to print out `size_t`. Newer Visual Studios and mingw-w64 toolchains built with the commit
// mentioned with c99 set as the standard supports the %z specifier properly.
//
// Related mingw-w64 commit: bfd33f6c0ec5e652cc9911857dd1492ece8d8383

#if (defined(_MSVC) && _MSVC < 1800) || (defined(__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO == 0)
#define AVIF_FMT_ZU "%Iu"
#else
Expand All @@ -23,7 +22,8 @@
void avifImageDump(avifImage * avif, uint32_t gridCols, uint32_t gridRows);
void avifContainerDump(avifDecoder * decoder);
void avifPrintVersions(void);
void avifDumpDiagnostics(struct avifDiagnostics * diag);
void avifDumpDiagnostics(const avifDiagnostics * diag);
int avifQueryCPUCount(void); // Returns 1 if it cannot query or fails to query

typedef enum avifAppFileFormat
{
Expand Down
1 change: 1 addition & 0 deletions contrib/gdk-pixbuf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ if(AVIF_BUILD_GDK_PIXBUF)
target_include_directories(pixbufloader-avif PUBLIC ${GDK_PIXBUF_INCLUDE_DIRS})

pkg_get_variable(GDK_PIXBUF_MODULEDIR gdk-pixbuf-2.0 gdk_pixbuf_moduledir)
string(REPLACE ${GDK_PIXBUF_PREFIX} ${CMAKE_INSTALL_PREFIX} GDK_PIXBUF_MODULEDIR ${GDK_PIXBUF_MODULEDIR})
install(TARGETS pixbufloader-avif DESTINATION ${GDK_PIXBUF_MODULEDIR})
else()
message(WARNING "gdk-pixbuf loader: disabled due to missing gdk-pixbuf-2.0")
Expand Down
2 changes: 1 addition & 1 deletion contrib/gdk-pixbuf/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ static gboolean avif_context_try_load(struct avif_context * context, GError ** e
if (image->transformFlags & AVIF_TRANSFORM_IMIR) {
GdkPixbuf *output_mirrored = NULL;

switch (image->imir.axis) {
switch (image->imir.mode) {
case 0:
output_mirrored = gdk_pixbuf_flip(output, FALSE);
break;
Expand Down
2 changes: 1 addition & 1 deletion ext/aom.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
: # If you're running this on Windows, be sure you've already run this (from your VC2019 install dir):
: # "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvars64.bat"

git clone -b v3.1.0 --depth 1 https://aomedia.googlesource.com/aom
git clone -b v3.1.1 --depth 1 https://aomedia.googlesource.com/aom

cd aom
mkdir build.libavif
Expand Down
Loading