| // Copyright 2020 Joe Drago. All rights reserved. | |
| // SPDX-License-Identifier: BSD-2-Clause | |
| #include "avif/internal.h" | |
| #include <assert.h> | |
| #include <string.h> | |
| static int calcMaxChannel(uint32_t depth, avifRange range) | |
| { | |
| int maxChannel = (int)((1 << depth) - 1); | |
| if (range == AVIF_RANGE_LIMITED) { | |
| maxChannel = avifFullToLimitedY(depth, maxChannel); | |
| } | |
| return maxChannel; | |
| } | |
| avifBool avifFillAlpha(const avifAlphaParams * const params) | |
| { | |
| if (params->dstDepth > 8) { | |
| const uint16_t maxChannel = (uint16_t)calcMaxChannel(params->dstDepth, params->dstRange); | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| *((uint16_t *)dstRow) = maxChannel; | |
| dstRow += params->dstPixelBytes; | |
| } | |
| } | |
| } else { | |
| const uint8_t maxChannel = (uint8_t)calcMaxChannel(params->dstDepth, params->dstRange); | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| *dstRow = maxChannel; | |
| dstRow += params->dstPixelBytes; | |
| } | |
| } | |
| } | |
| return AVIF_TRUE; | |
| } | |
| // Note: The [limited -> limited] paths are here for completeness, but in practice those | |
| // paths will never be used, as avifRGBImage is always full range. | |
| avifBool avifReformatAlpha(const avifAlphaParams * const params) | |
| { | |
| const int srcMaxChannel = (1 << params->srcDepth) - 1; | |
| const int dstMaxChannel = (1 << params->dstDepth) - 1; | |
| const float srcMaxChannelF = (float)srcMaxChannel; | |
| const float dstMaxChannelF = (float)dstMaxChannel; | |
| if (params->srcDepth == params->dstDepth) { | |
| // no depth rescale | |
| if ((params->srcRange == AVIF_RANGE_FULL) && (params->dstRange == AVIF_RANGE_FULL)) { | |
| // no depth rescale, no range conversion | |
| if (params->srcDepth > 8) { | |
| // no depth rescale, no range conversion, uint16_t -> uint16_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| *((uint16_t *)&dstRow[i * params->dstPixelBytes]) = *((uint16_t *)&srcRow[i * params->srcPixelBytes]); | |
| } | |
| } | |
| } else { | |
| // no depth rescale, no range conversion, uint8_t -> uint8_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| dstRow[i * params->dstPixelBytes] = srcRow[i * params->srcPixelBytes]; | |
| } | |
| } | |
| } | |
| } else if ((params->srcRange == AVIF_RANGE_LIMITED) && (params->dstRange == AVIF_RANGE_FULL)) { | |
| // limited -> full | |
| if (params->srcDepth > 8) { | |
| // no depth rescale, limited -> full, uint16_t -> uint16_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = *((uint16_t *)&srcRow[i * params->srcPixelBytes]); | |
| int dstAlpha = avifLimitedToFullY(params->srcDepth, srcAlpha); | |
| *((uint16_t *)&dstRow[i * params->dstPixelBytes]) = (uint16_t)dstAlpha; | |
| } | |
| } | |
| } else { | |
| // no depth rescale, limited -> full, uint8_t -> uint8_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = srcRow[i * params->srcPixelBytes]; | |
| int dstAlpha = avifLimitedToFullY(params->srcDepth, srcAlpha); | |
| dstRow[i * params->dstPixelBytes] = (uint8_t)dstAlpha; | |
| } | |
| } | |
| } | |
| } else if ((params->srcRange == AVIF_RANGE_FULL) && (params->dstRange == AVIF_RANGE_LIMITED)) { | |
| // full -> limited | |
| if (params->srcDepth > 8) { | |
| // no depth rescale, full -> limited, uint16_t -> uint16_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = *((uint16_t *)&srcRow[i * params->srcPixelBytes]); | |
| int dstAlpha = avifFullToLimitedY(params->dstDepth, srcAlpha); | |
| *((uint16_t *)&dstRow[i * params->dstPixelBytes]) = (uint16_t)dstAlpha; | |
| } | |
| } | |
| } else { | |
| // no depth rescale, full -> limited, uint8_t -> uint8_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = srcRow[i * params->srcPixelBytes]; | |
| int dstAlpha = avifFullToLimitedY(params->dstDepth, srcAlpha); | |
| dstRow[i * params->dstPixelBytes] = (uint8_t)dstAlpha; | |
| } | |
| } | |
| } | |
| } else if ((params->srcRange == AVIF_RANGE_LIMITED) && (params->dstRange == AVIF_RANGE_LIMITED)) { | |
| // limited -> limited | |
| if (params->srcDepth > 8) { | |
| // no depth rescale, limited -> limited, uint16_t -> uint16_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| *((uint16_t *)&dstRow[i * params->dstPixelBytes]) = *((uint16_t *)&srcRow[i * params->srcPixelBytes]); | |
| } | |
| } | |
| } else { | |
| // no depth rescale, limited -> limited, uint8_t -> uint8_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| dstRow[i * params->dstPixelBytes] = srcRow[i * params->srcPixelBytes]; | |
| } | |
| } | |
| } | |
| } | |
| } else { | |
| // depth rescale | |
| if ((params->srcRange == AVIF_RANGE_FULL) && (params->dstRange == AVIF_RANGE_FULL)) { | |
| // depth rescale, no range conversion | |
| if (params->srcDepth > 8) { | |
| if (params->dstDepth > 8) { | |
| // depth rescale, no range conversion, uint16_t -> uint16_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = *((uint16_t *)&srcRow[i * params->srcPixelBytes]); | |
| float alphaF = (float)srcAlpha / srcMaxChannelF; | |
| int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); | |
| dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); | |
| *((uint16_t *)&dstRow[i * params->dstPixelBytes]) = (uint16_t)dstAlpha; | |
| } | |
| } | |
| } else { | |
| // depth rescale, no range conversion, uint16_t -> uint8_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = *((uint16_t *)&srcRow[i * params->srcPixelBytes]); | |
| float alphaF = (float)srcAlpha / srcMaxChannelF; | |
| int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); | |
| dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); | |
| dstRow[i * params->dstPixelBytes] = (uint8_t)dstAlpha; | |
| } | |
| } | |
| } | |
| } else { | |
| // depth rescale, no range conversion, uint8_t -> uint16_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = srcRow[i * params->srcPixelBytes]; | |
| float alphaF = (float)srcAlpha / srcMaxChannelF; | |
| int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); | |
| dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); | |
| *((uint16_t *)&dstRow[i * params->dstPixelBytes]) = (uint16_t)dstAlpha; | |
| } | |
| } | |
| // If (srcDepth == 8), dstDepth must be >8 otherwise we'd be in the (params->srcDepth == params->dstDepth) block above. | |
| // assert(params->dstDepth > 8); | |
| } | |
| } else if ((params->srcRange == AVIF_RANGE_LIMITED) && (params->dstRange == AVIF_RANGE_FULL)) { | |
| // limited -> full | |
| if (params->srcDepth > 8) { | |
| if (params->dstDepth > 8) { | |
| // depth rescale, limited -> full, uint16_t -> uint16_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = *((uint16_t *)&srcRow[i * params->srcPixelBytes]); | |
| srcAlpha = avifLimitedToFullY(params->srcDepth, srcAlpha); | |
| float alphaF = (float)srcAlpha / srcMaxChannelF; | |
| int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); | |
| dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); | |
| *((uint16_t *)&dstRow[i * params->dstPixelBytes]) = (uint16_t)dstAlpha; | |
| } | |
| } | |
| } else { | |
| // depth rescale, limited -> full, uint16_t -> uint8_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = *((uint16_t *)&srcRow[i * params->srcPixelBytes]); | |
| srcAlpha = avifLimitedToFullY(params->srcDepth, srcAlpha); | |
| float alphaF = (float)srcAlpha / srcMaxChannelF; | |
| int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); | |
| dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); | |
| dstRow[i * params->dstPixelBytes] = (uint8_t)dstAlpha; | |
| } | |
| } | |
| } | |
| } else { | |
| // depth rescale, limited -> full, uint8_t -> uint16_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = srcRow[i * params->srcPixelBytes]; | |
| srcAlpha = avifLimitedToFullY(params->srcDepth, srcAlpha); | |
| float alphaF = (float)srcAlpha / srcMaxChannelF; | |
| int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); | |
| dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); | |
| *((uint16_t *)&dstRow[i * params->dstPixelBytes]) = (uint16_t)dstAlpha; | |
| } | |
| } | |
| // If (srcDepth == 8), dstDepth must be >8 otherwise we'd be in the (params->srcDepth == params->dstDepth) block above. | |
| // assert(params->dstDepth > 8); | |
| } | |
| } else if ((params->srcRange == AVIF_RANGE_FULL) && (params->dstRange == AVIF_RANGE_LIMITED)) { | |
| // full -> limited | |
| if (params->srcDepth > 8) { | |
| if (params->dstDepth > 8) { | |
| // depth rescale, full -> limited, uint16_t -> uint16_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = *((uint16_t *)&srcRow[i * params->srcPixelBytes]); | |
| float alphaF = (float)srcAlpha / srcMaxChannelF; | |
| int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); | |
| dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); | |
| dstAlpha = avifFullToLimitedY(params->dstDepth, dstAlpha); | |
| *((uint16_t *)&dstRow[i * params->dstPixelBytes]) = (uint16_t)dstAlpha; | |
| } | |
| } | |
| } else { | |
| // depth rescale, full -> limited, uint16_t -> uint8_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = *((uint16_t *)&srcRow[i * params->srcPixelBytes]); | |
| float alphaF = (float)srcAlpha / srcMaxChannelF; | |
| int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); | |
| dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); | |
| dstAlpha = avifFullToLimitedY(params->dstDepth, dstAlpha); | |
| dstRow[i * params->dstPixelBytes] = (uint8_t)dstAlpha; | |
| } | |
| } | |
| } | |
| } else { | |
| // depth rescale, full -> limited, uint8_t -> uint16_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = srcRow[i * params->srcPixelBytes]; | |
| float alphaF = (float)srcAlpha / srcMaxChannelF; | |
| int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); | |
| dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); | |
| dstAlpha = avifFullToLimitedY(params->dstDepth, dstAlpha); | |
| *((uint16_t *)&dstRow[i * params->dstPixelBytes]) = (uint16_t)dstAlpha; | |
| } | |
| } | |
| // If (srcDepth == 8), dstDepth must be >8 otherwise we'd be in the (params->srcDepth == params->dstDepth) block above. | |
| // assert(params->dstDepth > 8); | |
| } | |
| } else if ((params->srcRange == AVIF_RANGE_LIMITED) && (params->dstRange == AVIF_RANGE_LIMITED)) { | |
| // limited -> limited | |
| if (params->srcDepth > 8) { | |
| if (params->dstDepth > 8) { | |
| // depth rescale, limited -> limited, uint16_t -> uint16_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = *((uint16_t *)&srcRow[i * params->srcPixelBytes]); | |
| srcAlpha = avifLimitedToFullY(params->srcDepth, srcAlpha); | |
| float alphaF = (float)srcAlpha / srcMaxChannelF; | |
| int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); | |
| dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); | |
| dstAlpha = avifFullToLimitedY(params->dstDepth, dstAlpha); | |
| *((uint16_t *)&dstRow[i * params->dstPixelBytes]) = (uint16_t)dstAlpha; | |
| } | |
| } | |
| } else { | |
| // depth rescale, limited -> limited, uint16_t -> uint8_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = *((uint16_t *)&srcRow[i * params->srcPixelBytes]); | |
| srcAlpha = avifLimitedToFullY(params->srcDepth, srcAlpha); | |
| float alphaF = (float)srcAlpha / srcMaxChannelF; | |
| int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); | |
| dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); | |
| dstAlpha = avifFullToLimitedY(params->dstDepth, dstAlpha); | |
| dstRow[i * params->dstPixelBytes] = (uint8_t)dstAlpha; | |
| } | |
| } | |
| } | |
| } else { | |
| // depth rescale, limited -> limited, uint8_t -> uint16_t | |
| for (uint32_t j = 0; j < params->height; ++j) { | |
| uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes + (j * params->srcRowBytes)]; | |
| uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes + (j * params->dstRowBytes)]; | |
| for (uint32_t i = 0; i < params->width; ++i) { | |
| int srcAlpha = srcRow[i * params->srcPixelBytes]; | |
| srcAlpha = avifLimitedToFullY(params->srcDepth, srcAlpha); | |
| float alphaF = (float)srcAlpha / srcMaxChannelF; | |
| int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); | |
| dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); | |
| dstAlpha = avifFullToLimitedY(params->dstDepth, dstAlpha); | |
| *((uint16_t *)&dstRow[i * params->dstPixelBytes]) = (uint16_t)dstAlpha; | |
| } | |
| } | |
| // If (srcDepth == 8), dstDepth must be >8 otherwise we'd be in the (params->srcDepth == params->dstDepth) block above. | |
| // assert(params->dstDepth > 8); | |
| } | |
| } | |
| } | |
| return AVIF_TRUE; | |
| } | |
| avifResult avifRGBImagePremultiplyAlpha(avifRGBImage * rgb) | |
| { | |
| // no data | |
| if (!rgb->pixels || !rgb->rowBytes) { | |
| return AVIF_RESULT_REFORMAT_FAILED; | |
| } | |
| // no alpha. | |
| if (!avifRGBFormatHasAlpha(rgb->format)) { | |
| return AVIF_RESULT_INVALID_ARGUMENT; | |
| } | |
| avifResult libyuvResult = avifRGBImagePremultiplyAlphaLibYUV(rgb); | |
| if (libyuvResult != AVIF_RESULT_NOT_IMPLEMENTED) { | |
| return libyuvResult; | |
| } | |
| assert(rgb->depth >= 8 && rgb->depth <= 16); | |
| uint32_t max = (1 << rgb->depth) - 1; | |
| float maxF = (float)max; | |
| if (rgb->depth > 8) { | |
| if (rgb->format == AVIF_RGB_FORMAT_RGBA || rgb->format == AVIF_RGB_FORMAT_BGRA) { | |
| for (uint32_t j = 0; j < rgb->height; ++j) { | |
| uint8_t * row = &rgb->pixels[j * rgb->rowBytes]; | |
| for (uint32_t i = 0; i < rgb->width; ++i) { | |
| uint16_t * pixel = (uint16_t *)&row[i * 8]; | |
| uint16_t a = pixel[3]; | |
| if (a >= max) { | |
| // opaque is no-op | |
| continue; | |
| } else if (a == 0) { | |
| // result must be zero | |
| pixel[0] = 0; | |
| pixel[1] = 0; | |
| pixel[2] = 0; | |
| } else { | |
| // a < maxF is always true now, so we don't need clamp here | |
| pixel[0] = (uint16_t)avifRoundf((float)pixel[0] * (float)a / maxF); | |
| pixel[1] = (uint16_t)avifRoundf((float)pixel[1] * (float)a / maxF); | |
| pixel[2] = (uint16_t)avifRoundf((float)pixel[2] * (float)a / maxF); | |
| } | |
| } | |
| } | |
| } else { | |
| for (uint32_t j = 0; j < rgb->height; ++j) { | |
| uint8_t * row = &rgb->pixels[j * rgb->rowBytes]; | |
| for (uint32_t i = 0; i < rgb->width; ++i) { | |
| uint16_t * pixel = (uint16_t *)&row[i * 8]; | |
| uint16_t a = pixel[0]; | |
| if (a >= max) { | |
| continue; | |
| } else if (a == 0) { | |
| pixel[1] = 0; | |
| pixel[2] = 0; | |
| pixel[3] = 0; | |
| } else { | |
| pixel[1] = (uint16_t)avifRoundf((float)pixel[1] * (float)a / maxF); | |
| pixel[2] = (uint16_t)avifRoundf((float)pixel[2] * (float)a / maxF); | |
| pixel[3] = (uint16_t)avifRoundf((float)pixel[3] * (float)a / maxF); | |
| } | |
| } | |
| } | |
| } | |
| } else { | |
| if (rgb->format == AVIF_RGB_FORMAT_RGBA || rgb->format == AVIF_RGB_FORMAT_BGRA) { | |
| for (uint32_t j = 0; j < rgb->height; ++j) { | |
| uint8_t * row = &rgb->pixels[j * rgb->rowBytes]; | |
| for (uint32_t i = 0; i < rgb->width; ++i) { | |
| uint8_t * pixel = &row[i * 4]; | |
| uint8_t a = pixel[3]; | |
| // uint8_t can't exceed 255 | |
| if (a == max) { | |
| continue; | |
| } else if (a == 0) { | |
| pixel[0] = 0; | |
| pixel[1] = 0; | |
| pixel[2] = 0; | |
| } else { | |
| pixel[0] = (uint8_t)avifRoundf((float)pixel[0] * (float)a / maxF); | |
| pixel[1] = (uint8_t)avifRoundf((float)pixel[1] * (float)a / maxF); | |
| pixel[2] = (uint8_t)avifRoundf((float)pixel[2] * (float)a / maxF); | |
| } | |
| } | |
| } | |
| } else { | |
| for (uint32_t j = 0; j < rgb->height; ++j) { | |
| uint8_t * row = &rgb->pixels[j * rgb->rowBytes]; | |
| for (uint32_t i = 0; i < rgb->width; ++i) { | |
| uint8_t * pixel = &row[i * 4]; | |
| uint8_t a = pixel[0]; | |
| if (a == max) { | |
| continue; | |
| } else if (a == 0) { | |
| pixel[1] = 0; | |
| pixel[2] = 0; | |
| pixel[3] = 0; | |
| } else { | |
| pixel[1] = (uint8_t)avifRoundf((float)pixel[1] * (float)a / maxF); | |
| pixel[2] = (uint8_t)avifRoundf((float)pixel[2] * (float)a / maxF); | |
| pixel[3] = (uint8_t)avifRoundf((float)pixel[3] * (float)a / maxF); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return AVIF_RESULT_OK; | |
| } | |
| avifResult avifRGBImageUnpremultiplyAlpha(avifRGBImage * rgb) | |
| { | |
| // no data | |
| if (!rgb->pixels || !rgb->rowBytes) { | |
| return AVIF_RESULT_REFORMAT_FAILED; | |
| } | |
| // no alpha. | |
| if (!avifRGBFormatHasAlpha(rgb->format)) { | |
| return AVIF_RESULT_REFORMAT_FAILED; | |
| } | |
| avifResult libyuvResult = avifRGBImageUnpremultiplyAlphaLibYUV(rgb); | |
| if (libyuvResult != AVIF_RESULT_NOT_IMPLEMENTED) { | |
| return libyuvResult; | |
| } | |
| assert(rgb->depth >= 8 && rgb->depth <= 16); | |
| uint32_t max = (1 << rgb->depth) - 1; | |
| float maxF = (float)max; | |
| if (rgb->depth > 8) { | |
| if (rgb->format == AVIF_RGB_FORMAT_RGBA || rgb->format == AVIF_RGB_FORMAT_BGRA) { | |
| for (uint32_t j = 0; j < rgb->height; ++j) { | |
| uint8_t * row = &rgb->pixels[j * rgb->rowBytes]; | |
| for (uint32_t i = 0; i < rgb->width; ++i) { | |
| uint16_t * pixel = (uint16_t *)&row[i * 8]; | |
| uint16_t a = pixel[3]; | |
| if (a >= max) { | |
| // opaque is no-op | |
| continue; | |
| } else if (a == 0) { | |
| // prevent division by zero | |
| pixel[0] = 0; | |
| pixel[1] = 0; | |
| pixel[2] = 0; | |
| } else { | |
| float c1 = avifRoundf((float)pixel[0] * maxF / (float)a); | |
| float c2 = avifRoundf((float)pixel[1] * maxF / (float)a); | |
| float c3 = avifRoundf((float)pixel[2] * maxF / (float)a); | |
| pixel[0] = (uint16_t)AVIF_MIN(c1, maxF); | |
| pixel[1] = (uint16_t)AVIF_MIN(c2, maxF); | |
| pixel[2] = (uint16_t)AVIF_MIN(c3, maxF); | |
| } | |
| } | |
| } | |
| } else { | |
| for (uint32_t j = 0; j < rgb->height; ++j) { | |
| uint8_t * row = &rgb->pixels[j * rgb->rowBytes]; | |
| for (uint32_t i = 0; i < rgb->width; ++i) { | |
| uint16_t * pixel = (uint16_t *)&row[i * 8]; | |
| uint16_t a = pixel[0]; | |
| if (a >= max) { | |
| continue; | |
| } else if (a == 0) { | |
| pixel[1] = 0; | |
| pixel[2] = 0; | |
| pixel[3] = 0; | |
| } else { | |
| float c1 = avifRoundf((float)pixel[1] * maxF / (float)a); | |
| float c2 = avifRoundf((float)pixel[2] * maxF / (float)a); | |
| float c3 = avifRoundf((float)pixel[3] * maxF / (float)a); | |
| pixel[1] = (uint16_t)AVIF_MIN(c1, maxF); | |
| pixel[2] = (uint16_t)AVIF_MIN(c2, maxF); | |
| pixel[3] = (uint16_t)AVIF_MIN(c3, maxF); | |
| } | |
| } | |
| } | |
| } | |
| } else { | |
| if (rgb->format == AVIF_RGB_FORMAT_RGBA || rgb->format == AVIF_RGB_FORMAT_BGRA) { | |
| for (uint32_t j = 0; j < rgb->height; ++j) { | |
| uint8_t * row = &rgb->pixels[j * rgb->rowBytes]; | |
| for (uint32_t i = 0; i < rgb->width; ++i) { | |
| uint8_t * pixel = &row[i * 4]; | |
| uint8_t a = pixel[3]; | |
| if (a == max) { | |
| continue; | |
| } else if (a == 0) { | |
| pixel[0] = 0; | |
| pixel[1] = 0; | |
| pixel[2] = 0; | |
| } else { | |
| float c1 = avifRoundf((float)pixel[0] * maxF / (float)a); | |
| float c2 = avifRoundf((float)pixel[1] * maxF / (float)a); | |
| float c3 = avifRoundf((float)pixel[2] * maxF / (float)a); | |
| pixel[0] = (uint8_t)AVIF_MIN(c1, maxF); | |
| pixel[1] = (uint8_t)AVIF_MIN(c2, maxF); | |
| pixel[2] = (uint8_t)AVIF_MIN(c3, maxF); | |
| } | |
| } | |
| } | |
| } else { | |
| for (uint32_t j = 0; j < rgb->height; ++j) { | |
| uint8_t * row = &rgb->pixels[j * rgb->rowBytes]; | |
| for (uint32_t i = 0; i < rgb->width; ++i) { | |
| uint8_t * pixel = &row[i * 4]; | |
| uint8_t a = pixel[0]; | |
| if (a == max) { | |
| continue; | |
| } else if (a == 0) { | |
| pixel[1] = 0; | |
| pixel[2] = 0; | |
| pixel[3] = 0; | |
| } else { | |
| float c1 = avifRoundf((float)pixel[1] * maxF / (float)a); | |
| float c2 = avifRoundf((float)pixel[2] * maxF / (float)a); | |
| float c3 = avifRoundf((float)pixel[3] * maxF / (float)a); | |
| pixel[1] = (uint8_t)AVIF_MIN(c1, maxF); | |
| pixel[2] = (uint8_t)AVIF_MIN(c2, maxF); | |
| pixel[3] = (uint8_t)AVIF_MIN(c3, maxF); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return AVIF_RESULT_OK; | |
| } |