| // Copyright 2020 Joe Drago. All rights reserved. |
| // SPDX-License-Identifier: BSD-2-Clause |
| |
| #include "avif/internal.h" |
| |
| #include <assert.h> |
| #include <string.h> |
| |
| avifBool avifFillAlpha(const avifAlphaParams * params) |
| { |
| if (params->dstDepth > 8) { |
| const uint16_t maxChannel = (uint16_t)((1 << params->dstDepth) - 1); |
| 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 { |
| // In this case, (1 << params->dstDepth) - 1 is always equal to 255. |
| const uint8_t maxChannel = 255; |
| 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; |
| } |
| |
| avifBool avifReformatAlpha(const avifAlphaParams * 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->srcDepth > 8) { |
| // no depth rescale, uint16_t -> uint16_t |
| |
| for (uint32_t j = 0; j < params->height; ++j) { |
| const 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]) = *((const uint16_t *)&srcRow[i * params->srcPixelBytes]); |
| } |
| } |
| } else { |
| // no depth rescale, uint8_t -> uint8_t |
| |
| for (uint32_t j = 0; j < params->height; ++j) { |
| const 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->srcDepth > 8) { |
| if (params->dstDepth > 8) { |
| // depth rescale, uint16_t -> uint16_t |
| |
| for (uint32_t j = 0; j < params->height; ++j) { |
| const 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 = *((const 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, uint16_t -> uint8_t |
| |
| for (uint32_t j = 0; j < params->height; ++j) { |
| const 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 = *((const 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 { |
| // If (srcDepth == 8), dstDepth must be >8 otherwise we'd be in the (params->srcDepth == params->dstDepth) block above. |
| assert(params->dstDepth > 8); |
| |
| // depth rescale, uint8_t -> uint16_t |
| for (uint32_t j = 0; j < params->height; ++j) { |
| const 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; |
| } |
| } |
| } |
| } |
| |
| 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; |
| } |