|
9 | 9 | #import <Accelerate/Accelerate.h>
|
10 | 10 | #if __has_include(<libavif/avif.h>)
|
11 | 11 | #import <libavif/avif.h>
|
| 12 | +#import <libavif/internal.h> |
12 | 13 | #else
|
13 |
| -#import "avif/avif.h" |
| 14 | +#import "avif/avifs.h" |
| 15 | +#import "avif/internal.h" |
14 | 16 | #endif
|
15 | 17 | #import "Private/ColorSpace.h"
|
16 | 18 |
|
@@ -53,6 +55,146 @@ static CGImageRef CreateImageFromBuffer(avifImage * avif, vImage_Buffer* result)
|
53 | 55 | return imageRef;
|
54 | 56 | }
|
55 | 57 |
|
| 58 | +static avifBool avifPrepareReformatState(const avifImage * image, const avifRGBImage * rgb, avifReformatState * state) |
| 59 | +{ |
| 60 | + if ((image->depth != 8) && (image->depth != 10) && (image->depth != 12)) { |
| 61 | + return AVIF_FALSE; |
| 62 | + } |
| 63 | + if ((rgb->depth != 8) && (rgb->depth != 10) && (rgb->depth != 12) && (rgb->depth != 16)) { |
| 64 | + return AVIF_FALSE; |
| 65 | + } |
| 66 | + |
| 67 | + // These matrix coefficients values are currently unsupported. Revise this list as more support is added. |
| 68 | + // |
| 69 | + // YCgCo performs limited-full range adjustment on R,G,B but the current implementation performs range adjustment |
| 70 | + // on Y,U,V. So YCgCo with limited range is unsupported. |
| 71 | + if ((image->matrixCoefficients == 3 /* CICP reserved */) || |
| 72 | + ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_YCGCO) && (image->yuvRange == AVIF_RANGE_LIMITED)) || |
| 73 | + (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_BT2020_CL) || |
| 74 | + (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_SMPTE2085) || |
| 75 | + (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL) || |
| 76 | + (image->matrixCoefficients >= AVIF_MATRIX_COEFFICIENTS_ICTCP)) { // Note the >= catching "future" CICP values here too |
| 77 | + return AVIF_FALSE; |
| 78 | + } |
| 79 | + |
| 80 | + if ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) && (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV444)) { |
| 81 | + return AVIF_FALSE; |
| 82 | + } |
| 83 | + |
| 84 | + if (image->yuvFormat == AVIF_PIXEL_FORMAT_NONE) { |
| 85 | + return AVIF_FALSE; |
| 86 | + } |
| 87 | + |
| 88 | + avifGetPixelFormatInfo(image->yuvFormat, &state->formatInfo); |
| 89 | + avifCalcYUVCoefficients(image, &state->kr, &state->kg, &state->kb); |
| 90 | + state->mode = AVIF_REFORMAT_MODE_YUV_COEFFICIENTS; |
| 91 | + |
| 92 | + if (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) { |
| 93 | + state->mode = AVIF_REFORMAT_MODE_IDENTITY; |
| 94 | + } else if (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_YCGCO) { |
| 95 | + state->mode = AVIF_REFORMAT_MODE_YCGCO; |
| 96 | + } |
| 97 | + |
| 98 | + if (state->mode != AVIF_REFORMAT_MODE_YUV_COEFFICIENTS) { |
| 99 | + state->kr = 0.0f; |
| 100 | + state->kg = 0.0f; |
| 101 | + state->kb = 0.0f; |
| 102 | + } |
| 103 | + |
| 104 | + state->yuvChannelBytes = (image->depth > 8) ? 2 : 1; |
| 105 | + state->rgbChannelBytes = (rgb->depth > 8) ? 2 : 1; |
| 106 | + state->rgbChannelCount = avifRGBFormatChannelCount(rgb->format); |
| 107 | + state->rgbPixelBytes = state->rgbChannelBytes * state->rgbChannelCount; |
| 108 | + |
| 109 | + switch (rgb->format) { |
| 110 | + case AVIF_RGB_FORMAT_RGB: |
| 111 | + state->rgbOffsetBytesR = state->rgbChannelBytes * 0; |
| 112 | + state->rgbOffsetBytesG = state->rgbChannelBytes * 1; |
| 113 | + state->rgbOffsetBytesB = state->rgbChannelBytes * 2; |
| 114 | + state->rgbOffsetBytesA = 0; |
| 115 | + break; |
| 116 | + case AVIF_RGB_FORMAT_RGBA: |
| 117 | + state->rgbOffsetBytesR = state->rgbChannelBytes * 0; |
| 118 | + state->rgbOffsetBytesG = state->rgbChannelBytes * 1; |
| 119 | + state->rgbOffsetBytesB = state->rgbChannelBytes * 2; |
| 120 | + state->rgbOffsetBytesA = state->rgbChannelBytes * 3; |
| 121 | + break; |
| 122 | + case AVIF_RGB_FORMAT_ARGB: |
| 123 | + state->rgbOffsetBytesA = state->rgbChannelBytes * 0; |
| 124 | + state->rgbOffsetBytesR = state->rgbChannelBytes * 1; |
| 125 | + state->rgbOffsetBytesG = state->rgbChannelBytes * 2; |
| 126 | + state->rgbOffsetBytesB = state->rgbChannelBytes * 3; |
| 127 | + break; |
| 128 | + case AVIF_RGB_FORMAT_BGR: |
| 129 | + state->rgbOffsetBytesB = state->rgbChannelBytes * 0; |
| 130 | + state->rgbOffsetBytesG = state->rgbChannelBytes * 1; |
| 131 | + state->rgbOffsetBytesR = state->rgbChannelBytes * 2; |
| 132 | + state->rgbOffsetBytesA = 0; |
| 133 | + break; |
| 134 | + case AVIF_RGB_FORMAT_BGRA: |
| 135 | + state->rgbOffsetBytesB = state->rgbChannelBytes * 0; |
| 136 | + state->rgbOffsetBytesG = state->rgbChannelBytes * 1; |
| 137 | + state->rgbOffsetBytesR = state->rgbChannelBytes * 2; |
| 138 | + state->rgbOffsetBytesA = state->rgbChannelBytes * 3; |
| 139 | + break; |
| 140 | + case AVIF_RGB_FORMAT_ABGR: |
| 141 | + state->rgbOffsetBytesA = state->rgbChannelBytes * 0; |
| 142 | + state->rgbOffsetBytesB = state->rgbChannelBytes * 1; |
| 143 | + state->rgbOffsetBytesG = state->rgbChannelBytes * 2; |
| 144 | + state->rgbOffsetBytesR = state->rgbChannelBytes * 3; |
| 145 | + break; |
| 146 | + |
| 147 | + default: |
| 148 | + return AVIF_FALSE; |
| 149 | + } |
| 150 | + |
| 151 | + state->yuvDepth = image->depth; |
| 152 | + state->yuvRange = image->yuvRange; |
| 153 | + state->yuvMaxChannel = (1 << image->depth) - 1; |
| 154 | + state->rgbMaxChannel = (1 << rgb->depth) - 1; |
| 155 | + state->rgbMaxChannelF = (float)state->rgbMaxChannel; |
| 156 | + state->biasY = (state->yuvRange == AVIF_RANGE_LIMITED) ? (float)(16 << (state->yuvDepth - 8)) : 0.0f; |
| 157 | + state->biasUV = (float)(1 << (state->yuvDepth - 1)); |
| 158 | + state->biasA = (image->alphaRange == AVIF_RANGE_LIMITED) ? (float)(16 << (state->yuvDepth - 8)) : 0.0f; |
| 159 | + state->rangeY = (float)((state->yuvRange == AVIF_RANGE_LIMITED) ? (219 << (state->yuvDepth - 8)) : state->yuvMaxChannel); |
| 160 | + state->rangeUV = (float)((state->yuvRange == AVIF_RANGE_LIMITED) ? (224 << (state->yuvDepth - 8)) : state->yuvMaxChannel); |
| 161 | + state->rangeA = (float)((image->alphaRange == AVIF_RANGE_LIMITED) ? (219 << (state->yuvDepth - 8)) : state->yuvMaxChannel); |
| 162 | + |
| 163 | + uint32_t cpCount = 1 << image->depth; |
| 164 | + if (state->mode == AVIF_REFORMAT_MODE_IDENTITY) { |
| 165 | + for (uint32_t cp = 0; cp < cpCount; ++cp) { |
| 166 | + state->unormFloatTableY[cp] = ((float)cp - state->biasY) / state->rangeY; |
| 167 | + state->unormFloatTableUV[cp] = ((float)cp - state->biasY) / state->rangeY; |
| 168 | + } |
| 169 | + } else { |
| 170 | + for (uint32_t cp = 0; cp < cpCount; ++cp) { |
| 171 | + // Review this when implementing YCgCo limited range support. |
| 172 | + state->unormFloatTableY[cp] = ((float)cp - state->biasY) / state->rangeY; |
| 173 | + state->unormFloatTableUV[cp] = ((float)cp - state->biasUV) / state->rangeUV; |
| 174 | + } |
| 175 | + } |
| 176 | + |
| 177 | + state->toRGBAlphaMode = AVIF_ALPHA_MULTIPLY_MODE_NO_OP; |
| 178 | + if (image->alphaPlane) { |
| 179 | + if (!avifRGBFormatHasAlpha(rgb->format) || rgb->ignoreAlpha) { |
| 180 | + // if we are converting some image with alpha into a format without alpha, we should do 'premultiply alpha' before |
| 181 | + // discarding alpha plane. This has the same effect of rendering this image on a black background, which makes sense. |
| 182 | + if (!image->alphaPremultiplied) { |
| 183 | + state->toRGBAlphaMode = AVIF_ALPHA_MULTIPLY_MODE_MULTIPLY; |
| 184 | + } |
| 185 | + } else { |
| 186 | + if (!image->alphaPremultiplied && rgb->alphaPremultiplied) { |
| 187 | + state->toRGBAlphaMode = AVIF_ALPHA_MULTIPLY_MODE_MULTIPLY; |
| 188 | + } else if (image->alphaPremultiplied && !rgb->alphaPremultiplied) { |
| 189 | + state->toRGBAlphaMode = AVIF_ALPHA_MULTIPLY_MODE_UNMULTIPLY; |
| 190 | + } |
| 191 | + } |
| 192 | + } |
| 193 | + |
| 194 | + return AVIF_TRUE; |
| 195 | +} |
| 196 | + |
| 197 | + |
56 | 198 | static void SetupConversionInfo(avifImage * avif,
|
57 | 199 | avifReformatState* state,
|
58 | 200 | vImage_YpCbCrToARGBMatrix* matrix,
|
|
0 commit comments