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

Experimental AVIF bit depth extension #1215

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
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 include/avif/avif.h
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,9 @@ typedef enum avifStrictFlag
// https://crbug.com/1246678.
AVIF_STRICT_ALPHA_ISPE_REQUIRED = (1 << 2),

// TODO(yguyon): Generalize AVIF_STRICT_ALPHA_ISPE_REQUIRED to bit depth extension
// or add AVIF_STRICT_SUBDEPTH_ISPE_REQUIRED

// Maximum strictness; enables all bits above. This is avifDecoder's default.
AVIF_STRICT_ENABLED = AVIF_STRICT_PIXI_REQUIRED | AVIF_STRICT_CLAP_VALID | AVIF_STRICT_ALPHA_ISPE_REQUIRED
} avifStrictFlag;
Expand Down
27 changes: 26 additions & 1 deletion include/avif/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ extern "C" {
#define AVIF_URN_ALPHA0 "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"
#define AVIF_URN_ALPHA1 "urn:mpeg:hevc:2015:auxid:1"

// TODO(yguyon): Add reference to specification
#define AVIF_URN_SUBDEPTH_8LSB "urn:mpeg:mpegB:cicp:systems:auxiliary:8lsb"

#define AVIF_CONTENT_TYPE_XMP "application/rdf+xml"

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -92,7 +95,29 @@ void avifImageCopyNoAlloc(avifImage * dstImage, const avifImage * srcImage);
// Copies the samples from srcImage to dstImage. dstImage must be allocated.
// srcImage and dstImage must have the same width, height, and depth.
// If the AVIF_PLANES_YUV bit is set in planes, then srcImage and dstImage must have the same yuvFormat and yuvRange.
void avifImageCopySamples(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes);
avifResult avifImageCopySamples(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes);

// ---------------------------------------------------------------------------
// Subdepth

typedef enum avifSubdepthMode
{
// The image can be encoded as is with an AV1 codec.
AVIF_SUBDEPTH_NONE,
// The image is split into two images because its bit depth is not supported by AV1.
AVIF_SUBDEPTH_8_MOST_SIGNIFICANT_BITS,
AVIF_SUBDEPTH_8_LEAST_SIGNIFICANT_BITS
} avifSubdepthMode;

// Same as avifImageCopySamples() with source and destination bit masks and shifts.
avifResult avifImageCopySamplesExtended(avifImage * dstImage,
enum avifSubdepthMode dstSubdepthMode,
const avifImage * srcImage,
enum avifSubdepthMode srcSubdepthMode,
avifPlanesFlags planes);

// ---------------------------------------------------------------------------
// Alpha

typedef struct avifAlphaParams
{
Expand Down
102 changes: 88 additions & 14 deletions src/avif.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,43 @@ void avifImageCopyNoAlloc(avifImage * dstImage, const avifImage * srcImage)
dstImage->imir = srcImage->imir;
}

void avifImageCopySamples(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes)
avifResult avifImageCopySamples(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes)
{
assert(srcImage->depth == dstImage->depth);
if (planes & AVIF_PLANES_YUV) {
assert((srcImage->yuvFormat == dstImage->yuvFormat) && (srcImage->yuvRange == dstImage->yuvRange));
return avifImageCopySamplesExtended(dstImage, AVIF_SUBDEPTH_NONE, srcImage, AVIF_SUBDEPTH_NONE, planes);
}

avifResult avifImageCopySamplesExtended(avifImage * dstImage,
avifSubdepthMode dstSubdepthMode,
const avifImage * srcImage,
avifSubdepthMode srcSubdepthMode,
avifPlanesFlags planes)
{
if (!(planes & AVIF_PLANES_YUV) && !(planes & AVIF_PLANES_A)) {
// Early exit.
return AVIF_RESULT_OK;
}
AVIF_CHECKERR((srcSubdepthMode == AVIF_SUBDEPTH_NONE) || (dstSubdepthMode == AVIF_SUBDEPTH_NONE), AVIF_RESULT_UNSUPPORTED_DEPTH);
AVIF_CHECKERR((srcSubdepthMode == AVIF_SUBDEPTH_NONE) || (dstSubdepthMode == AVIF_SUBDEPTH_NONE), AVIF_RESULT_UNSUPPORTED_DEPTH);
if (((srcImage->depth != 8) && (srcImage->depth != 10) && (srcImage->depth != 12) && (srcImage->depth != 16)) ||
((dstImage->depth != 8) && (dstImage->depth != 10) && (dstImage->depth != 12) && (dstImage->depth != 16))) {
return AVIF_RESULT_UNSUPPORTED_DEPTH;
}
if ((srcSubdepthMode == AVIF_SUBDEPTH_8_MOST_SIGNIFICANT_BITS) || (srcSubdepthMode == AVIF_SUBDEPTH_8_LEAST_SIGNIFICANT_BITS)) {
if (srcImage->depth != 8) {
return AVIF_RESULT_INVALID_ARGUMENT;
}
if (dstImage->depth == 8) {
return avifImageCopySamplesExtended(dstImage, AVIF_SUBDEPTH_NONE, srcImage, AVIF_SUBDEPTH_NONE, planes);
}
} else if ((dstSubdepthMode == AVIF_SUBDEPTH_8_MOST_SIGNIFICANT_BITS) || (dstSubdepthMode == AVIF_SUBDEPTH_8_LEAST_SIGNIFICANT_BITS)) {
if (dstImage->depth != 8) {
return AVIF_RESULT_INVALID_ARGUMENT;
}
if (srcImage->depth == 8) {
return avifImageCopySamplesExtended(dstImage, AVIF_SUBDEPTH_NONE, srcImage, AVIF_SUBDEPTH_NONE, planes);
}
} else if ((srcSubdepthMode != AVIF_SUBDEPTH_NONE) || (dstSubdepthMode != AVIF_SUBDEPTH_NONE)) {
return AVIF_RESULT_UNSUPPORTED_DEPTH; // Only 8MSB and 8LSB are supported.
}
const size_t bytesPerPixel = avifImageUsesU16(srcImage) ? 2 : 1;

Expand All @@ -206,20 +238,62 @@ void avifImageCopySamples(avifImage * dstImage, const avifImage * srcImage, avif
uint8_t * dstRow = avifImagePlane(dstImage, c);
const uint32_t srcRowBytes = avifImagePlaneRowBytes(srcImage, c);
const uint32_t dstRowBytes = avifImagePlaneRowBytes(dstImage, c);
assert(!srcRow == !dstRow);
AVIF_CHECKERR(!srcRow == !dstRow, AVIF_RESULT_INVALID_ARGUMENT);
if (!srcRow) {
continue;
}
assert(planeWidth == avifImagePlaneWidth(dstImage, c));
assert(planeHeight == avifImagePlaneHeight(dstImage, c));

const size_t planeWidthBytes = planeWidth * bytesPerPixel;
for (uint32_t y = 0; y < planeHeight; ++y) {
memcpy(dstRow, srcRow, planeWidthBytes);
srcRow += srcRowBytes;
dstRow += dstRowBytes;
AVIF_CHECKERR(planeWidth == avifImagePlaneWidth(dstImage, c), AVIF_RESULT_INVALID_ARGUMENT);
AVIF_CHECKERR(planeHeight == avifImagePlaneHeight(dstImage, c), AVIF_RESULT_INVALID_ARGUMENT);

if ((srcSubdepthMode == AVIF_SUBDEPTH_8_MOST_SIGNIFICANT_BITS) || (srcSubdepthMode == AVIF_SUBDEPTH_8_LEAST_SIGNIFICANT_BITS)) {
// Copy samples that represent a part of the final bits to their place in the reconstructed buffer.
uint32_t srcShift;
uint16_t dstMask;
if (srcSubdepthMode == AVIF_SUBDEPTH_8_MOST_SIGNIFICANT_BITS) {
srcShift = dstImage->depth - 8;
dstMask = (1u << (dstImage->depth - 8)) - 1u;
} else {
srcShift = 0;
dstMask = ((1u << (dstImage->depth - 8)) - 1u) << 8;
}
for (uint32_t y = 0; y < planeHeight; ++y) {
uint16_t * dstRow16 = (uint16_t *)dstRow;
for (uint32_t x = 0; x < planeWidth; ++x) {
dstRow16[x] = (dstRow16[x] & dstMask) | (srcRow[x] << srcShift);
}
srcRow += srcRowBytes;
dstRow += dstRowBytes;
}
} else if ((dstSubdepthMode == AVIF_SUBDEPTH_8_MOST_SIGNIFICANT_BITS) ||
(dstSubdepthMode == AVIF_SUBDEPTH_8_LEAST_SIGNIFICANT_BITS)) {
// Copy the most or least significant bits of the source samples to a buffer with a lesser bit depth.
uint32_t srcShift;
uint16_t srcMask;
if (dstSubdepthMode == AVIF_SUBDEPTH_8_MOST_SIGNIFICANT_BITS) {
srcShift = srcImage->depth - 8;
srcMask = (1u << 8) - 1u;
} else {
srcShift = 0;
srcMask = (1u << 8) - 1u;
}
for (uint32_t y = 0; y < planeHeight; ++y) {
const uint16_t * srcRow16 = (uint16_t *)srcRow;
for (uint32_t x = 0; x < planeWidth; ++x) {
dstRow[x] = (uint8_t)((srcRow16[x] >> srcShift) & srcMask);
}
srcRow += srcRowBytes;
dstRow += dstRowBytes;
}
} else {
const size_t planeWidthBytes = planeWidth * bytesPerPixel;
for (uint32_t y = 0; y < planeHeight; ++y) {
memcpy(dstRow, srcRow, planeWidthBytes);
srcRow += srcRowBytes;
dstRow += dstRowBytes;
}
}
}
return AVIF_RESULT_OK;
}

avifResult avifImageCopy(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes)
Expand Down Expand Up @@ -248,7 +322,7 @@ avifResult avifImageCopy(avifImage * dstImage, const avifImage * srcImage, avifP
return allocationResult;
}
}
avifImageCopySamples(dstImage, srcImage, planes);
AVIF_CHECKRES(avifImageCopySamples(dstImage, srcImage, planes));
return AVIF_RESULT_OK;
}

Expand Down
Loading