Skip to content

Commit

Permalink
Make avifImageScale function public
Browse files Browse the repository at this point in the history
This is useful for apps to scale images (without having to
worry about bitdepth, subsampling, etc).

This commit renames avifImageScale to avifImageScaleWithLimit. The
latter would remain an internal only function while the former is
now part of the public ABI (moved to avif.h)

Potential workaround for issue AOMediaCodec#1484.
  • Loading branch information
vigneshvg committed Oct 5, 2023
1 parent cb6fdc3 commit 55aab4a
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 41 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
This part of AV1 encoder is not as thoroughly tested, so there are higher
possibility encoder may crash when given certain configuration or input.
* Add imageSequenceTrackPresent flag to the avifDecoder struct.
* avifImageScale() function was made part of the public ABI.

### Changed
* Update aom.cmd: v3.7.0
Expand Down
7 changes: 7 additions & 0 deletions include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,13 @@ AVIF_API void avifImageStealPlanes(avifImage * dstImage, avifImage * srcImage, a
// the number of threads that can ever be in flight at a given time, please account for this
// accordingly.

// ---------------------------------------------------------------------------
// Scaling

// Scales the YUV/A planes in-place. dstWidth and dstHeight must both be <= AVIF_DEFAULT_IMAGE_DIMENSION_LIMIT and
// dstWidth*dstHeight should be <= AVIF_DEFAULT_IMAGE_SIZE_LIMIT.
AVIF_API avifResult avifImageScale(avifImage * image, uint32_t dstWidth, uint32_t dstHeight, avifDiagnostics * diag);

// ---------------------------------------------------------------------------
// Optional YUV<->RGB support

Expand Down
14 changes: 7 additions & 7 deletions include/avif/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,13 +251,13 @@ void avifSetTileConfiguration(int threads, uint32_t width, uint32_t height, int
// ---------------------------------------------------------------------------
// Scaling

// This scales the YUV/A planes in-place.
avifBool avifImageScale(avifImage * image,
uint32_t dstWidth,
uint32_t dstHeight,
uint32_t imageSizeLimit,
uint32_t imageDimensionLimit,
avifDiagnostics * diag);
// Scales the YUV/A planes in-place.
avifResult avifImageScaleWithLimit(avifImage * image,
uint32_t dstWidth,
uint32_t dstHeight,
uint32_t imageSizeLimit,
uint32_t imageDimensionLimit,
avifDiagnostics * diag);

// ---------------------------------------------------------------------------
// AVIF item category
Expand Down
14 changes: 7 additions & 7 deletions src/read.c
Original file line number Diff line number Diff line change
Expand Up @@ -4980,13 +4980,13 @@ static avifResult avifDecoderDecodeTiles(avifDecoder * decoder, uint32_t nextIma

// Scale the decoded image so that it corresponds to this tile's output dimensions
if ((tile->width != tile->image->width) || (tile->height != tile->image->height)) {
if (!avifImageScale(tile->image,
tile->width,
tile->height,
decoder->imageSizeLimit,
decoder->imageDimensionLimit,
&decoder->diag)) {
avifDiagnosticsPrintf(&decoder->diag, "avifImageScale() failed");
if (avifImageScaleWithLimit(tile->image,
tile->width,
tile->height,
decoder->imageSizeLimit,
decoder->imageDimensionLimit,
&decoder->diag) != AVIF_RESULT_OK) {
avifDiagnosticsPrintf(&decoder->diag, "avifImageScaleWithLimit() failed");
return avifGetErrorForItemCategory(tile->input->itemCategory);
}
}
Expand Down
42 changes: 24 additions & 18 deletions src/scale.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,25 @@
// This should be configurable and/or smarter. kFilterBox has the highest quality but is the slowest.
#define AVIF_LIBYUV_FILTER_MODE kFilterBox

avifBool avifImageScale(avifImage * image,
uint32_t dstWidth,
uint32_t dstHeight,
uint32_t imageSizeLimit,
uint32_t imageDimensionLimit,
avifDiagnostics * diag)
avifResult avifImageScaleWithLimit(avifImage * image,
uint32_t dstWidth,
uint32_t dstHeight,
uint32_t imageSizeLimit,
uint32_t imageDimensionLimit,
avifDiagnostics * diag)
{
if ((image->width == dstWidth) && (image->height == dstHeight)) {
// Nothing to do
return AVIF_TRUE;
return AVIF_RESULT_OK;
}

if ((dstWidth == 0) || (dstHeight == 0)) {
avifDiagnosticsPrintf(diag, "avifImageScale requested invalid dst dimensions [%ux%u]", dstWidth, dstHeight);
return AVIF_FALSE;
avifDiagnosticsPrintf(diag, "avifImageScaleWithLimit requested invalid dst dimensions [%ux%u]", dstWidth, dstHeight);
return AVIF_RESULT_INVALID_ARGUMENT;
}
if (avifDimensionsTooLarge(dstWidth, dstHeight, imageSizeLimit, imageDimensionLimit)) {
avifDiagnosticsPrintf(diag, "avifImageScale requested dst dimensions that are too large [%ux%u]", dstWidth, dstHeight);
return AVIF_FALSE;
avifDiagnosticsPrintf(diag, "avifImageScaleWithLimit requested dst dimensions that are too large [%ux%u]", dstWidth, dstHeight);
return AVIF_RESULT_NOT_IMPLEMENTED;
}

uint8_t * srcYUVPlanes[AVIF_PLANE_COUNT_YUV];
Expand Down Expand Up @@ -71,20 +71,20 @@ avifBool avifImageScale(avifImage * image,
// A simple conservative check to avoid integer overflows in libyuv's ScalePlane() and
// ScalePlane_12() functions.
if (srcWidth > 16384) {
avifDiagnosticsPrintf(diag, "avifImageScale requested invalid width scale for libyuv [%u -> %u]", srcWidth, dstWidth);
return AVIF_FALSE;
avifDiagnosticsPrintf(diag, "avifImageScaleWithLimit requested invalid width scale for libyuv [%u -> %u]", srcWidth, dstWidth);
return AVIF_RESULT_NOT_IMPLEMENTED;
}
if (srcHeight > 16384) {
avifDiagnosticsPrintf(diag, "avifImageScale requested invalid height scale for libyuv [%u -> %u]", srcHeight, dstHeight);
return AVIF_FALSE;
avifDiagnosticsPrintf(diag, "avifImageScaleWithLimit requested invalid height scale for libyuv [%u -> %u]", srcHeight, dstHeight);
return AVIF_RESULT_NOT_IMPLEMENTED;
}
}

if (srcYUVPlanes[0]) {
const avifResult allocationResult = avifImageAllocatePlanes(image, AVIF_PLANES_YUV);
if (allocationResult != AVIF_RESULT_OK) {
avifDiagnosticsPrintf(diag, "Allocation of YUV planes failed: %s", avifResultToString(allocationResult));
return AVIF_FALSE;
return AVIF_RESULT_OUT_OF_MEMORY;
}

for (int i = 0; i < AVIF_PLANE_COUNT_YUV; ++i) {
Expand Down Expand Up @@ -124,7 +124,7 @@ avifBool avifImageScale(avifImage * image,
const avifResult allocationResult = avifImageAllocatePlanes(image, AVIF_PLANES_A);
if (allocationResult != AVIF_RESULT_OK) {
avifDiagnosticsPrintf(diag, "Allocation of alpha plane failed: %s", avifResultToString(allocationResult));
return AVIF_FALSE;
return AVIF_RESULT_OUT_OF_MEMORY;
}

if (image->depth > 8) {
Expand All @@ -150,5 +150,11 @@ avifBool avifImageScale(avifImage * image,
}
}

return AVIF_TRUE;
return AVIF_RESULT_OK;
}

avifResult avifImageScale(avifImage * image, uint32_t dstWidth, uint32_t dstHeight, avifDiagnostics * diag)
{
avifDiagnosticsClearError(diag);
return avifImageScaleWithLimit(image, dstWidth, dstHeight, AVIF_DEFAULT_IMAGE_SIZE_LIMIT, AVIF_DEFAULT_IMAGE_DIMENSION_LIMIT, diag);
}
32 changes: 23 additions & 9 deletions tests/gtest/avifscaletest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ namespace {
// Used to pass the data folder path to the GoogleTest suites.
const char* data_path = nullptr;

constexpr bool kIgnoreMetadata = true;

//------------------------------------------------------------------------------

class ScaleTest
Expand All @@ -30,7 +32,6 @@ TEST_P(ScaleTest, Roundtrip) {
const avifPixelFormat yuv_format = std::get<1>(GetParam());
const bool create_alpha = std::get<2>(GetParam());

const bool kIgnoreMetadata = true;
const testutil::AvifImagePtr image =
testutil::ReadImage(data_path, "paris_exif_xmp_icc.jpg", yuv_format,
bit_depth, AVIF_CHROMA_DOWNSAMPLING_BEST_QUALITY,
Expand All @@ -50,19 +51,17 @@ TEST_P(ScaleTest, Roundtrip) {

const uint32_t scaled_width = static_cast<uint32_t>(image->width * 0.9);
const uint32_t scaled_height = static_cast<uint32_t>(image->height * 2.14);
const uint32_t kImageSizeLimit = std::numeric_limits<uint32_t>::max();
const uint32_t kImageDimensionLimit = std::numeric_limits<uint32_t>::max();
avifDiagnostics diag;
avifDiagnosticsClearError(&diag);
ASSERT_TRUE(avifImageScale(scaled_image.get(), scaled_width, scaled_height,
kImageSizeLimit, kImageDimensionLimit, &diag))
ASSERT_EQ(
avifImageScale(scaled_image.get(), scaled_width, scaled_height, &diag),
AVIF_RESULT_OK)
<< diag.error;
EXPECT_EQ(scaled_image->width, scaled_width);
EXPECT_EQ(scaled_image->height, scaled_height);

avifDiagnosticsClearError(&diag);
ASSERT_TRUE(avifImageScale(scaled_image.get(), image->width, image->height,
kImageSizeLimit, kImageDimensionLimit, &diag))
ASSERT_EQ(
avifImageScale(scaled_image.get(), image->width, image->height, &diag),
AVIF_RESULT_OK)
<< diag.error;
EXPECT_EQ(scaled_image->width, image->width);
EXPECT_EQ(scaled_image->height, image->height);
Expand All @@ -79,6 +78,21 @@ INSTANTIATE_TEST_SUITE_P(
AVIF_PIXEL_FORMAT_YUV420, AVIF_PIXEL_FORMAT_YUV400),
/*create_alpha=*/Values(true, false)));

TEST(ScaleTest, LargerThanDefaultLimits) {
const testutil::AvifImagePtr image = testutil::ReadImage(
data_path, "paris_exif_xmp_icc.jpg", AVIF_PIXEL_FORMAT_YUV420, 8,
AVIF_CHROMA_DOWNSAMPLING_BEST_QUALITY, kIgnoreMetadata, kIgnoreMetadata,
kIgnoreMetadata);
ASSERT_NE(image, nullptr);
avifDiagnostics diag;
// dstWidth*dstHeight is larger than AVIF_DEFAULT_IMAGE_SIZE_LIMIT.
EXPECT_NE(avifImageScale(image.get(), 20000, 20000, &diag), AVIF_RESULT_OK);
// dstWidth is larger than AVIF_DEFAULT_IMAGE_DIMENSION_LIMIT.
EXPECT_NE(avifImageScale(image.get(), 40000, 2, &diag), AVIF_RESULT_OK);
// dstHeight is larger than AVIF_DEFAULT_IMAGE_DIMENSION_LIMIT.
EXPECT_NE(avifImageScale(image.get(), 2, 40000, &diag), AVIF_RESULT_OK);
}

//------------------------------------------------------------------------------

} // namespace
Expand Down

0 comments on commit 55aab4a

Please sign in to comment.