| OLD | NEW |
| 1 // Copyright 2014 Google Inc. All Rights Reserved. | 1 // Copyright 2014 Google Inc. All Rights Reserved. |
| 2 // | 2 // |
| 3 // Use of this source code is governed by a BSD-style license | 3 // Use of this source code is governed by a BSD-style license |
| 4 // that can be found in the COPYING file in the root of the source | 4 // that can be found in the COPYING file in the root of the source |
| 5 // tree. An additional intellectual property rights grant can be found | 5 // tree. An additional intellectual property rights grant can be found |
| 6 // in the file PATENTS. All contributing project authors may | 6 // in the file PATENTS. All contributing project authors may |
| 7 // be found in the AUTHORS file in the root of the source tree. | 7 // be found in the AUTHORS file in the root of the source tree. |
| 8 // ----------------------------------------------------------------------------- | 8 // ----------------------------------------------------------------------------- |
| 9 // | 9 // |
| 10 // WebPPicture utils for colorspace conversion | 10 // WebPPicture utils for colorspace conversion |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 | 25 |
| 26 // If defined, use table to compute x / alpha. | 26 // If defined, use table to compute x / alpha. |
| 27 #define USE_INVERSE_ALPHA_TABLE | 27 #define USE_INVERSE_ALPHA_TABLE |
| 28 | 28 |
| 29 static const union { | 29 static const union { |
| 30 uint32_t argb; | 30 uint32_t argb; |
| 31 uint8_t bytes[4]; | 31 uint8_t bytes[4]; |
| 32 } test_endian = { 0xff000000u }; | 32 } test_endian = { 0xff000000u }; |
| 33 #define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff) | 33 #define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff) |
| 34 | 34 |
| 35 static WEBP_INLINE uint32_t MakeARGB32(int a, int r, int g, int b) { | |
| 36 return (((uint32_t)a << 24) | (r << 16) | (g << 8) | b); | |
| 37 } | |
| 38 | |
| 39 //------------------------------------------------------------------------------ | 35 //------------------------------------------------------------------------------ |
| 40 // Detection of non-trivial transparency | 36 // Detection of non-trivial transparency |
| 41 | 37 |
| 42 // Returns true if alpha[] has non-0xff values. | 38 // Returns true if alpha[] has non-0xff values. |
| 43 static int CheckNonOpaque(const uint8_t* alpha, int width, int height, | 39 static int CheckNonOpaque(const uint8_t* alpha, int width, int height, |
| 44 int x_step, int y_step) { | 40 int x_step, int y_step) { |
| 45 if (alpha == NULL) return 0; | 41 if (alpha == NULL) return 0; |
| 46 while (height-- > 0) { | 42 while (height-- > 0) { |
| 47 int x; | 43 int x; |
| 48 for (x = 0; x < width * x_step; x += x_step) { | 44 for (x = 0; x < width * x_step; x += x_step) { |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 82 #define kGamma 0.80 // for now we use a different gamma value than kGammaF | 78 #define kGamma 0.80 // for now we use a different gamma value than kGammaF |
| 83 #define kGammaFix 12 // fixed-point precision for linear values | 79 #define kGammaFix 12 // fixed-point precision for linear values |
| 84 #define kGammaScale ((1 << kGammaFix) - 1) | 80 #define kGammaScale ((1 << kGammaFix) - 1) |
| 85 #define kGammaTabFix 7 // fixed-point fractional bits precision | 81 #define kGammaTabFix 7 // fixed-point fractional bits precision |
| 86 #define kGammaTabScale (1 << kGammaTabFix) | 82 #define kGammaTabScale (1 << kGammaTabFix) |
| 87 #define kGammaTabRounder (kGammaTabScale >> 1) | 83 #define kGammaTabRounder (kGammaTabScale >> 1) |
| 88 #define kGammaTabSize (1 << (kGammaFix - kGammaTabFix)) | 84 #define kGammaTabSize (1 << (kGammaFix - kGammaTabFix)) |
| 89 | 85 |
| 90 static int kLinearToGammaTab[kGammaTabSize + 1]; | 86 static int kLinearToGammaTab[kGammaTabSize + 1]; |
| 91 static uint16_t kGammaToLinearTab[256]; | 87 static uint16_t kGammaToLinearTab[256]; |
| 92 static int kGammaTablesOk = 0; | 88 static volatile int kGammaTablesOk = 0; |
| 93 | 89 |
| 94 static void InitGammaTables(void) { | 90 static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTables(void) { |
| 95 if (!kGammaTablesOk) { | 91 if (!kGammaTablesOk) { |
| 96 int v; | 92 int v; |
| 97 const double scale = (double)(1 << kGammaTabFix) / kGammaScale; | 93 const double scale = (double)(1 << kGammaTabFix) / kGammaScale; |
| 98 const double norm = 1. / 255.; | 94 const double norm = 1. / 255.; |
| 99 for (v = 0; v <= 255; ++v) { | 95 for (v = 0; v <= 255; ++v) { |
| 100 kGammaToLinearTab[v] = | 96 kGammaToLinearTab[v] = |
| 101 (uint16_t)(pow(norm * v, kGamma) * kGammaScale + .5); | 97 (uint16_t)(pow(norm * v, kGamma) * kGammaScale + .5); |
| 102 } | 98 } |
| 103 for (v = 0; v <= kGammaTabSize; ++v) { | 99 for (v = 0; v <= kGammaTabSize; ++v) { |
| 104 kLinearToGammaTab[v] = (int)(255. * pow(scale * v, 1. / kGamma) + .5); | 100 kLinearToGammaTab[v] = (int)(255. * pow(scale * v, 1. / kGamma) + .5); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 123 | 119 |
| 124 // Convert a linear value 'v' to YUV_FIX+2 fixed-point precision | 120 // Convert a linear value 'v' to YUV_FIX+2 fixed-point precision |
| 125 // U/V value, suitable for RGBToU/V calls. | 121 // U/V value, suitable for RGBToU/V calls. |
| 126 static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) { | 122 static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) { |
| 127 const int y = Interpolate(base_value << shift); // final uplifted value | 123 const int y = Interpolate(base_value << shift); // final uplifted value |
| 128 return (y + kGammaTabRounder) >> kGammaTabFix; // descale | 124 return (y + kGammaTabRounder) >> kGammaTabFix; // descale |
| 129 } | 125 } |
| 130 | 126 |
| 131 #else | 127 #else |
| 132 | 128 |
| 133 static void InitGammaTables(void) {} | 129 static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTables(void) {} |
| 134 static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { return v; } | 130 static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { return v; } |
| 135 static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) { | 131 static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) { |
| 136 return (int)(base_value << shift); | 132 return (int)(base_value << shift); |
| 137 } | 133 } |
| 138 | 134 |
| 139 #endif // USE_GAMMA_COMPRESSION | 135 #endif // USE_GAMMA_COMPRESSION |
| 140 | 136 |
| 141 //------------------------------------------------------------------------------ | 137 //------------------------------------------------------------------------------ |
| 142 // RGB -> YUV conversion | 138 // RGB -> YUV conversion |
| 143 | 139 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 155 return (rg == NULL) ? VP8RGBToV(r, g, b, YUV_HALF << 2) | 151 return (rg == NULL) ? VP8RGBToV(r, g, b, YUV_HALF << 2) |
| 156 : VP8RGBToV(r, g, b, VP8RandomBits(rg, YUV_FIX + 2)); | 152 : VP8RGBToV(r, g, b, VP8RandomBits(rg, YUV_FIX + 2)); |
| 157 } | 153 } |
| 158 | 154 |
| 159 //------------------------------------------------------------------------------ | 155 //------------------------------------------------------------------------------ |
| 160 // Smart RGB->YUV conversion | 156 // Smart RGB->YUV conversion |
| 161 | 157 |
| 162 static const int kNumIterations = 6; | 158 static const int kNumIterations = 6; |
| 163 static const int kMinDimensionIterativeConversion = 4; | 159 static const int kMinDimensionIterativeConversion = 4; |
| 164 | 160 |
| 165 // We use a-priori a different precision for storing RGB and Y/W components | 161 // We could use SFIX=0 and only uint8_t for fixed_y_t, but it produces some |
| 166 // We could use YFIX=0 and only uint8_t for fixed_y_t, but it produces some | |
| 167 // banding sometimes. Better use extra precision. | 162 // banding sometimes. Better use extra precision. |
| 168 // TODO(skal): cleanup once TFIX/YFIX values are fixed. | 163 #define SFIX 2 // fixed-point precision of RGB and Y/W |
| 164 typedef int16_t fixed_t; // signed type with extra SFIX precision for UV |
| 165 typedef uint16_t fixed_y_t; // unsigned type with extra SFIX precision for W |
| 169 | 166 |
| 170 typedef int16_t fixed_t; // signed type with extra TFIX precision for UV | 167 #define SHALF (1 << SFIX >> 1) |
| 171 typedef uint16_t fixed_y_t; // unsigned type with extra YFIX precision for W | 168 #define MAX_Y_T ((256 << SFIX) - 1) |
| 172 #define TFIX 6 // fixed-point precision of RGB | 169 #define SROUNDER (1 << (YUV_FIX + SFIX - 1)) |
| 173 #define YFIX 2 // fixed point precision for Y/W | |
| 174 | |
| 175 #define THALF ((1 << TFIX) >> 1) | |
| 176 #define MAX_Y_T ((256 << YFIX) - 1) | |
| 177 #define TROUNDER (1 << (YUV_FIX + TFIX - 1)) | |
| 178 | 170 |
| 179 #if defined(USE_GAMMA_COMPRESSION) | 171 #if defined(USE_GAMMA_COMPRESSION) |
| 180 | 172 |
| 181 // float variant of gamma-correction | 173 // float variant of gamma-correction |
| 182 // We use tables of different size and precision, along with a 'real-world' | 174 // We use tables of different size and precision, along with a 'real-world' |
| 183 // Gamma value close to ~2. | 175 // Gamma value close to ~2. |
| 184 #define kGammaF 2.2 | 176 #define kGammaF 2.2 |
| 185 static float kGammaToLinearTabF[MAX_Y_T + 1]; // size scales with Y_FIX | 177 static float kGammaToLinearTabF[MAX_Y_T + 1]; // size scales with Y_FIX |
| 186 static float kLinearToGammaTabF[kGammaTabSize + 2]; | 178 static float kLinearToGammaTabF[kGammaTabSize + 2]; |
| 187 static int kGammaTablesFOk = 0; | 179 static volatile int kGammaTablesFOk = 0; |
| 188 | 180 |
| 189 static void InitGammaTablesF(void) { | 181 static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) { |
| 190 if (!kGammaTablesFOk) { | 182 if (!kGammaTablesFOk) { |
| 191 int v; | 183 int v; |
| 192 const double norm = 1. / MAX_Y_T; | 184 const double norm = 1. / MAX_Y_T; |
| 193 const double scale = 1. / kGammaTabSize; | 185 const double scale = 1. / kGammaTabSize; |
| 194 for (v = 0; v <= MAX_Y_T; ++v) { | 186 for (v = 0; v <= MAX_Y_T; ++v) { |
| 195 kGammaToLinearTabF[v] = (float)pow(norm * v, kGammaF); | 187 kGammaToLinearTabF[v] = (float)pow(norm * v, kGammaF); |
| 196 } | 188 } |
| 197 for (v = 0; v <= kGammaTabSize; ++v) { | 189 for (v = 0; v <= kGammaTabSize; ++v) { |
| 198 kLinearToGammaTabF[v] = (float)(MAX_Y_T * pow(scale * v, 1. / kGammaF)); | 190 kLinearToGammaTabF[v] = (float)(MAX_Y_T * pow(scale * v, 1. / kGammaF)); |
| 199 } | 191 } |
| 200 // to prevent small rounding errors to cause read-overflow: | 192 // to prevent small rounding errors to cause read-overflow: |
| 201 kLinearToGammaTabF[kGammaTabSize + 1] = kLinearToGammaTabF[kGammaTabSize]; | 193 kLinearToGammaTabF[kGammaTabSize + 1] = kLinearToGammaTabF[kGammaTabSize]; |
| 202 kGammaTablesFOk = 1; | 194 kGammaTablesFOk = 1; |
| 203 } | 195 } |
| 204 } | 196 } |
| 205 | 197 |
| 206 static WEBP_INLINE float GammaToLinearF(int v) { | 198 static WEBP_INLINE float GammaToLinearF(int v) { |
| 207 return kGammaToLinearTabF[v]; | 199 return kGammaToLinearTabF[v]; |
| 208 } | 200 } |
| 209 | 201 |
| 210 static WEBP_INLINE float LinearToGammaF(float value) { | 202 static WEBP_INLINE int LinearToGammaF(float value) { |
| 211 const float v = value * kGammaTabSize; | 203 const float v = value * kGammaTabSize; |
| 212 const int tab_pos = (int)v; | 204 const int tab_pos = (int)v; |
| 213 const float x = v - (float)tab_pos; // fractional part | 205 const float x = v - (float)tab_pos; // fractional part |
| 214 const float v0 = kLinearToGammaTabF[tab_pos + 0]; | 206 const float v0 = kLinearToGammaTabF[tab_pos + 0]; |
| 215 const float v1 = kLinearToGammaTabF[tab_pos + 1]; | 207 const float v1 = kLinearToGammaTabF[tab_pos + 1]; |
| 216 const float y = v1 * x + v0 * (1.f - x); // interpolate | 208 const float y = v1 * x + v0 * (1.f - x); // interpolate |
| 217 return y; | 209 return (int)(y + .5); |
| 218 } | 210 } |
| 219 | 211 |
| 220 #else | 212 #else |
| 221 | 213 |
| 222 static void InitGammaTablesF(void) {} | 214 static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) {} |
| 223 static WEBP_INLINE float GammaToLinearF(int v) { | 215 static WEBP_INLINE float GammaToLinearF(int v) { |
| 224 const float norm = 1.f / MAX_Y_T; | 216 const float norm = 1.f / MAX_Y_T; |
| 225 return norm * v; | 217 return norm * v; |
| 226 } | 218 } |
| 227 static WEBP_INLINE float LinearToGammaF(float value) { | 219 static WEBP_INLINE int LinearToGammaF(float value) { |
| 228 return MAX_Y_T * value; | 220 return (int)(MAX_Y_T * value + .5); |
| 229 } | 221 } |
| 230 | 222 |
| 231 #endif // USE_GAMMA_COMPRESSION | 223 #endif // USE_GAMMA_COMPRESSION |
| 232 | 224 |
| 233 //------------------------------------------------------------------------------ | 225 //------------------------------------------------------------------------------ |
| 234 | 226 |
| 235 // precision: YFIX -> TFIX | |
| 236 static WEBP_INLINE int FixedYToW(int v) { | |
| 237 #if TFIX == YFIX | |
| 238 return v; | |
| 239 #elif TFIX >= YFIX | |
| 240 return v << (TFIX - YFIX); | |
| 241 #else | |
| 242 return v >> (YFIX - TFIX); | |
| 243 #endif | |
| 244 } | |
| 245 | |
| 246 static WEBP_INLINE int FixedWToY(int v) { | |
| 247 #if TFIX == YFIX | |
| 248 return v; | |
| 249 #elif YFIX >= TFIX | |
| 250 return v << (YFIX - TFIX); | |
| 251 #else | |
| 252 return v >> (TFIX - YFIX); | |
| 253 #endif | |
| 254 } | |
| 255 | |
| 256 static uint8_t clip_8b(fixed_t v) { | 227 static uint8_t clip_8b(fixed_t v) { |
| 257 return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u; | 228 return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u; |
| 258 } | 229 } |
| 259 | 230 |
| 260 static fixed_y_t clip_y(int y) { | 231 static fixed_y_t clip_y(int y) { |
| 261 return (!(y & ~MAX_Y_T)) ? (fixed_y_t)y : (y < 0) ? 0 : MAX_Y_T; | 232 return (!(y & ~MAX_Y_T)) ? (fixed_y_t)y : (y < 0) ? 0 : MAX_Y_T; |
| 262 } | 233 } |
| 263 | 234 |
| 264 // precision: TFIX -> YFIX | |
| 265 static fixed_y_t clip_fixed_t(fixed_t v) { | |
| 266 const int y = FixedWToY(v); | |
| 267 const fixed_y_t w = clip_y(y); | |
| 268 return w; | |
| 269 } | |
| 270 | |
| 271 //------------------------------------------------------------------------------ | 235 //------------------------------------------------------------------------------ |
| 272 | 236 |
| 273 static int RGBToGray(int r, int g, int b) { | 237 static int RGBToGray(int r, int g, int b) { |
| 274 const int luma = 19595 * r + 38470 * g + 7471 * b + YUV_HALF; | 238 const int luma = 19595 * r + 38470 * g + 7471 * b + YUV_HALF; |
| 275 return (luma >> YUV_FIX); | 239 return (luma >> YUV_FIX); |
| 276 } | 240 } |
| 277 | 241 |
| 278 static float RGBToGrayF(float r, float g, float b) { | 242 static float RGBToGrayF(float r, float g, float b) { |
| 279 return 0.299f * r + 0.587f * g + 0.114f * b; | 243 return 0.299f * r + 0.587f * g + 0.114f * b; |
| 280 } | 244 } |
| 281 | 245 |
| 282 static float ScaleDown(int a, int b, int c, int d) { | 246 static int ScaleDown(int a, int b, int c, int d) { |
| 283 const float A = GammaToLinearF(a); | 247 const float A = GammaToLinearF(a); |
| 284 const float B = GammaToLinearF(b); | 248 const float B = GammaToLinearF(b); |
| 285 const float C = GammaToLinearF(c); | 249 const float C = GammaToLinearF(c); |
| 286 const float D = GammaToLinearF(d); | 250 const float D = GammaToLinearF(d); |
| 287 return LinearToGammaF(0.25f * (A + B + C + D)); | 251 return LinearToGammaF(0.25f * (A + B + C + D)); |
| 288 } | 252 } |
| 289 | 253 |
| 290 static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int len) { | 254 static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int len) { |
| 291 while (len-- > 0) { | 255 while (len-- > 0) { |
| 292 const float R = GammaToLinearF(src[0]); | 256 const float R = GammaToLinearF(src[0]); |
| 293 const float G = GammaToLinearF(src[1]); | 257 const float G = GammaToLinearF(src[1]); |
| 294 const float B = GammaToLinearF(src[2]); | 258 const float B = GammaToLinearF(src[2]); |
| 295 const float Y = RGBToGrayF(R, G, B); | 259 const float Y = RGBToGrayF(R, G, B); |
| 296 *dst++ = (fixed_y_t)(LinearToGammaF(Y) + .5); | 260 *dst++ = (fixed_y_t)LinearToGammaF(Y); |
| 297 src += 3; | 261 src += 3; |
| 298 } | 262 } |
| 299 } | 263 } |
| 300 | 264 |
| 301 static WEBP_INLINE void UpdateChroma(const fixed_y_t* src1, | 265 static int UpdateChroma(const fixed_y_t* src1, |
| 302 const fixed_y_t* src2, | 266 const fixed_y_t* src2, |
| 303 fixed_t* dst, fixed_y_t* tmp, int len) { | 267 fixed_t* dst, fixed_y_t* tmp, int len) { |
| 268 int diff = 0; |
| 304 while (len--> 0) { | 269 while (len--> 0) { |
| 305 const float r = ScaleDown(src1[0], src1[3], src2[0], src2[3]); | 270 const int r = ScaleDown(src1[0], src1[3], src2[0], src2[3]); |
| 306 const float g = ScaleDown(src1[1], src1[4], src2[1], src2[4]); | 271 const int g = ScaleDown(src1[1], src1[4], src2[1], src2[4]); |
| 307 const float b = ScaleDown(src1[2], src1[5], src2[2], src2[5]); | 272 const int b = ScaleDown(src1[2], src1[5], src2[2], src2[5]); |
| 308 const float W = RGBToGrayF(r, g, b); | 273 const int W = RGBToGray(r, g, b); |
| 309 dst[0] = (fixed_t)FixedYToW((int)(r - W)); | 274 const int r_avg = (src1[0] + src1[3] + src2[0] + src2[3] + 2) >> 2; |
| 310 dst[1] = (fixed_t)FixedYToW((int)(g - W)); | 275 const int g_avg = (src1[1] + src1[4] + src2[1] + src2[4] + 2) >> 2; |
| 311 dst[2] = (fixed_t)FixedYToW((int)(b - W)); | 276 const int b_avg = (src1[2] + src1[5] + src2[2] + src2[5] + 2) >> 2; |
| 277 dst[0] = (fixed_t)(r - W); |
| 278 dst[1] = (fixed_t)(g - W); |
| 279 dst[2] = (fixed_t)(b - W); |
| 312 dst += 3; | 280 dst += 3; |
| 313 src1 += 6; | 281 src1 += 6; |
| 314 src2 += 6; | 282 src2 += 6; |
| 315 if (tmp != NULL) { | 283 if (tmp != NULL) { |
| 316 tmp[0] = tmp[1] = clip_y((int)(W + .5)); | 284 tmp[0] = tmp[1] = clip_y(W); |
| 317 tmp += 2; | 285 tmp += 2; |
| 318 } | 286 } |
| 287 diff += abs(RGBToGray(r_avg, g_avg, b_avg) - W); |
| 319 } | 288 } |
| 289 return diff; |
| 320 } | 290 } |
| 321 | 291 |
| 322 //------------------------------------------------------------------------------ | 292 //------------------------------------------------------------------------------ |
| 323 | 293 |
| 324 static WEBP_INLINE int Filter(const fixed_t* const A, const fixed_t* const B, | 294 static WEBP_INLINE int Filter(const fixed_t* const A, const fixed_t* const B, |
| 325 int rightwise) { | 295 int rightwise) { |
| 326 int v; | 296 int v; |
| 327 if (!rightwise) { | 297 if (!rightwise) { |
| 328 v = (A[0] * 9 + A[-3] * 3 + B[0] * 3 + B[-3]); | 298 v = (A[0] * 9 + A[-3] * 3 + B[0] * 3 + B[-3]); |
| 329 } else { | 299 } else { |
| 330 v = (A[0] * 9 + A[+3] * 3 + B[0] * 3 + B[+3]); | 300 v = (A[0] * 9 + A[+3] * 3 + B[0] * 3 + B[+3]); |
| 331 } | 301 } |
| 332 return (v + 8) >> 4; | 302 return (v + 8) >> 4; |
| 333 } | 303 } |
| 334 | 304 |
| 335 static WEBP_INLINE int Filter2(int A, int B) { return (A * 3 + B + 2) >> 2; } | 305 static WEBP_INLINE int Filter2(int A, int B) { return (A * 3 + B + 2) >> 2; } |
| 336 | 306 |
| 337 //------------------------------------------------------------------------------ | 307 //------------------------------------------------------------------------------ |
| 338 | 308 |
| 339 // 8bit -> YFIX | 309 static WEBP_INLINE fixed_y_t UpLift(uint8_t a) { // 8bit -> SFIX |
| 340 static WEBP_INLINE fixed_y_t UpLift(uint8_t a) { | 310 return ((fixed_y_t)a << SFIX) | SHALF; |
| 341 return ((fixed_y_t)a << YFIX) | (1 << (YFIX - 1)); | |
| 342 } | 311 } |
| 343 | 312 |
| 344 static void ImportOneRow(const uint8_t* const r_ptr, | 313 static void ImportOneRow(const uint8_t* const r_ptr, |
| 345 const uint8_t* const g_ptr, | 314 const uint8_t* const g_ptr, |
| 346 const uint8_t* const b_ptr, | 315 const uint8_t* const b_ptr, |
| 347 int step, | 316 int step, |
| 348 int pic_width, | 317 int pic_width, |
| 349 fixed_y_t* const dst) { | 318 fixed_y_t* const dst) { |
| 350 int i; | 319 int i; |
| 351 for (i = 0; i < pic_width; ++i) { | 320 for (i = 0; i < pic_width; ++i) { |
| 352 const int off = i * step; | 321 const int off = i * step; |
| 353 dst[3 * i + 0] = UpLift(r_ptr[off]); | 322 dst[3 * i + 0] = UpLift(r_ptr[off]); |
| 354 dst[3 * i + 1] = UpLift(g_ptr[off]); | 323 dst[3 * i + 1] = UpLift(g_ptr[off]); |
| 355 dst[3 * i + 2] = UpLift(b_ptr[off]); | 324 dst[3 * i + 2] = UpLift(b_ptr[off]); |
| 356 } | 325 } |
| 357 if (pic_width & 1) { // replicate rightmost pixel | 326 if (pic_width & 1) { // replicate rightmost pixel |
| 358 memcpy(dst + 3 * pic_width, dst + 3 * (pic_width - 1), 3 * sizeof(*dst)); | 327 memcpy(dst + 3 * pic_width, dst + 3 * (pic_width - 1), 3 * sizeof(*dst)); |
| 359 } | 328 } |
| 360 } | 329 } |
| 361 | 330 |
| 362 static void InterpolateTwoRows(const fixed_y_t* const best_y, | 331 static void InterpolateTwoRows(const fixed_y_t* const best_y, |
| 363 const fixed_t* const prev_uv, | 332 const fixed_t* const prev_uv, |
| 364 const fixed_t* const cur_uv, | 333 const fixed_t* const cur_uv, |
| 365 const fixed_t* const next_uv, | 334 const fixed_t* const next_uv, |
| 366 int w, | 335 int w, |
| 367 fixed_y_t* const out1, | 336 fixed_y_t* const out1, |
| 368 fixed_y_t* const out2) { | 337 fixed_y_t* const out2) { |
| 369 int i, k; | 338 int i, k; |
| 370 { // special boundary case for i==0 | 339 { // special boundary case for i==0 |
| 371 const int W0 = FixedYToW(best_y[0]); | 340 const int W0 = best_y[0]; |
| 372 const int W1 = FixedYToW(best_y[w]); | 341 const int W1 = best_y[w]; |
| 373 for (k = 0; k <= 2; ++k) { | 342 for (k = 0; k <= 2; ++k) { |
| 374 out1[k] = clip_fixed_t(Filter2(cur_uv[k], prev_uv[k]) + W0); | 343 out1[k] = clip_y(Filter2(cur_uv[k], prev_uv[k]) + W0); |
| 375 out2[k] = clip_fixed_t(Filter2(cur_uv[k], next_uv[k]) + W1); | 344 out2[k] = clip_y(Filter2(cur_uv[k], next_uv[k]) + W1); |
| 376 } | 345 } |
| 377 } | 346 } |
| 378 for (i = 1; i < w - 1; ++i) { | 347 for (i = 1; i < w - 1; ++i) { |
| 379 const int W0 = FixedYToW(best_y[i + 0]); | 348 const int W0 = best_y[i + 0]; |
| 380 const int W1 = FixedYToW(best_y[i + w]); | 349 const int W1 = best_y[i + w]; |
| 381 const int off = 3 * (i >> 1); | 350 const int off = 3 * (i >> 1); |
| 382 for (k = 0; k <= 2; ++k) { | 351 for (k = 0; k <= 2; ++k) { |
| 383 const int tmp0 = Filter(cur_uv + off + k, prev_uv + off + k, i & 1); | 352 const int tmp0 = Filter(cur_uv + off + k, prev_uv + off + k, i & 1); |
| 384 const int tmp1 = Filter(cur_uv + off + k, next_uv + off + k, i & 1); | 353 const int tmp1 = Filter(cur_uv + off + k, next_uv + off + k, i & 1); |
| 385 out1[3 * i + k] = clip_fixed_t(tmp0 + W0); | 354 out1[3 * i + k] = clip_y(tmp0 + W0); |
| 386 out2[3 * i + k] = clip_fixed_t(tmp1 + W1); | 355 out2[3 * i + k] = clip_y(tmp1 + W1); |
| 387 } | 356 } |
| 388 } | 357 } |
| 389 { // special boundary case for i == w - 1 | 358 { // special boundary case for i == w - 1 |
| 390 const int W0 = FixedYToW(best_y[i + 0]); | 359 const int W0 = best_y[i + 0]; |
| 391 const int W1 = FixedYToW(best_y[i + w]); | 360 const int W1 = best_y[i + w]; |
| 392 const int off = 3 * (i >> 1); | 361 const int off = 3 * (i >> 1); |
| 393 for (k = 0; k <= 2; ++k) { | 362 for (k = 0; k <= 2; ++k) { |
| 394 out1[3 * i + k] = | 363 out1[3 * i + k] = clip_y(Filter2(cur_uv[off + k], prev_uv[off + k]) + W0); |
| 395 clip_fixed_t(Filter2(cur_uv[off + k], prev_uv[off + k]) + W0); | 364 out2[3 * i + k] = clip_y(Filter2(cur_uv[off + k], next_uv[off + k]) + W1); |
| 396 out2[3 * i + k] = | |
| 397 clip_fixed_t(Filter2(cur_uv[off + k], next_uv[off + k]) + W1); | |
| 398 } | 365 } |
| 399 } | 366 } |
| 400 } | 367 } |
| 401 | 368 |
| 402 static WEBP_INLINE uint8_t ConvertRGBToY(int r, int g, int b) { | 369 static WEBP_INLINE uint8_t ConvertRGBToY(int r, int g, int b) { |
| 403 const int luma = 16839 * r + 33059 * g + 6420 * b + TROUNDER; | 370 const int luma = 16839 * r + 33059 * g + 6420 * b + SROUNDER; |
| 404 return clip_8b(16 + (luma >> (YUV_FIX + TFIX))); | 371 return clip_8b(16 + (luma >> (YUV_FIX + SFIX))); |
| 405 } | 372 } |
| 406 | 373 |
| 407 static WEBP_INLINE uint8_t ConvertRGBToU(int r, int g, int b) { | 374 static WEBP_INLINE uint8_t ConvertRGBToU(int r, int g, int b) { |
| 408 const int u = -9719 * r - 19081 * g + 28800 * b + TROUNDER; | 375 const int u = -9719 * r - 19081 * g + 28800 * b + SROUNDER; |
| 409 return clip_8b(128 + (u >> (YUV_FIX + TFIX))); | 376 return clip_8b(128 + (u >> (YUV_FIX + SFIX))); |
| 410 } | 377 } |
| 411 | 378 |
| 412 static WEBP_INLINE uint8_t ConvertRGBToV(int r, int g, int b) { | 379 static WEBP_INLINE uint8_t ConvertRGBToV(int r, int g, int b) { |
| 413 const int v = +28800 * r - 24116 * g - 4684 * b + TROUNDER; | 380 const int v = +28800 * r - 24116 * g - 4684 * b + SROUNDER; |
| 414 return clip_8b(128 + (v >> (YUV_FIX + TFIX))); | 381 return clip_8b(128 + (v >> (YUV_FIX + SFIX))); |
| 415 } | 382 } |
| 416 | 383 |
| 417 static int ConvertWRGBToYUV(const fixed_y_t* const best_y, | 384 static int ConvertWRGBToYUV(const fixed_y_t* const best_y, |
| 418 const fixed_t* const best_uv, | 385 const fixed_t* const best_uv, |
| 419 WebPPicture* const picture) { | 386 WebPPicture* const picture) { |
| 420 int i, j; | 387 int i, j; |
| 421 const int w = (picture->width + 1) & ~1; | 388 const int w = (picture->width + 1) & ~1; |
| 422 const int h = (picture->height + 1) & ~1; | 389 const int h = (picture->height + 1) & ~1; |
| 423 const int uv_w = w >> 1; | 390 const int uv_w = w >> 1; |
| 424 const int uv_h = h >> 1; | 391 const int uv_h = h >> 1; |
| 425 for (j = 0; j < picture->height; ++j) { | 392 for (j = 0; j < picture->height; ++j) { |
| 426 for (i = 0; i < picture->width; ++i) { | 393 for (i = 0; i < picture->width; ++i) { |
| 427 const int off = 3 * ((i >> 1) + (j >> 1) * uv_w); | 394 const int off = 3 * ((i >> 1) + (j >> 1) * uv_w); |
| 428 const int off2 = i + j * picture->y_stride; | 395 const int off2 = i + j * picture->y_stride; |
| 429 const int W = FixedYToW(best_y[i + j * w]); | 396 const int W = best_y[i + j * w]; |
| 430 const int r = best_uv[off + 0] + W; | 397 const int r = best_uv[off + 0] + W; |
| 431 const int g = best_uv[off + 1] + W; | 398 const int g = best_uv[off + 1] + W; |
| 432 const int b = best_uv[off + 2] + W; | 399 const int b = best_uv[off + 2] + W; |
| 433 picture->y[off2] = ConvertRGBToY(r, g, b); | 400 picture->y[off2] = ConvertRGBToY(r, g, b); |
| 434 } | 401 } |
| 435 } | 402 } |
| 436 for (j = 0; j < uv_h; ++j) { | 403 for (j = 0; j < uv_h; ++j) { |
| 437 uint8_t* const dst_u = picture->u + j * picture->uv_stride; | 404 uint8_t* const dst_u = picture->u + j * picture->uv_stride; |
| 438 uint8_t* const dst_v = picture->v + j * picture->uv_stride; | 405 uint8_t* const dst_v = picture->v + j * picture->uv_stride; |
| 439 for (i = 0; i < uv_w; ++i) { | 406 for (i = 0; i < uv_w; ++i) { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 468 // TODO(skal): allocate one big memory chunk. But for now, it's easier | 435 // TODO(skal): allocate one big memory chunk. But for now, it's easier |
| 469 // for valgrind debugging to have several chunks. | 436 // for valgrind debugging to have several chunks. |
| 470 fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t); // scratch | 437 fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t); // scratch |
| 471 fixed_y_t* const best_y = SAFE_ALLOC(w, h, fixed_y_t); | 438 fixed_y_t* const best_y = SAFE_ALLOC(w, h, fixed_y_t); |
| 472 fixed_y_t* const target_y = SAFE_ALLOC(w, h, fixed_y_t); | 439 fixed_y_t* const target_y = SAFE_ALLOC(w, h, fixed_y_t); |
| 473 fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t); | 440 fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t); |
| 474 fixed_t* const best_uv = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t); | 441 fixed_t* const best_uv = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t); |
| 475 fixed_t* const target_uv = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t); | 442 fixed_t* const target_uv = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t); |
| 476 fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t); | 443 fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t); |
| 477 int ok; | 444 int ok; |
| 445 int diff_sum = 0; |
| 446 const int first_diff_threshold = (int)(2.5 * w * h); |
| 447 const int min_improvement = 5; // stop if improvement is below this % |
| 448 const int min_first_improvement = 80; |
| 478 | 449 |
| 479 if (best_y == NULL || best_uv == NULL || | 450 if (best_y == NULL || best_uv == NULL || |
| 480 target_y == NULL || target_uv == NULL || | 451 target_y == NULL || target_uv == NULL || |
| 481 best_rgb_y == NULL || best_rgb_uv == NULL || | 452 best_rgb_y == NULL || best_rgb_uv == NULL || |
| 482 tmp_buffer == NULL) { | 453 tmp_buffer == NULL) { |
| 483 ok = WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); | 454 ok = WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); |
| 484 goto End; | 455 goto End; |
| 485 } | 456 } |
| 486 assert(picture->width >= kMinDimensionIterativeConversion); | 457 assert(picture->width >= kMinDimensionIterativeConversion); |
| 487 assert(picture->height >= kMinDimensionIterativeConversion); | 458 assert(picture->height >= kMinDimensionIterativeConversion); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 500 ImportOneRow(r_ptr + off1, g_ptr + off1, b_ptr + off1, | 471 ImportOneRow(r_ptr + off1, g_ptr + off1, b_ptr + off1, |
| 501 step, picture->width, src1); | 472 step, picture->width, src1); |
| 502 if (!is_last_row) { | 473 if (!is_last_row) { |
| 503 ImportOneRow(r_ptr + off2, g_ptr + off2, b_ptr + off2, | 474 ImportOneRow(r_ptr + off2, g_ptr + off2, b_ptr + off2, |
| 504 step, picture->width, src2); | 475 step, picture->width, src2); |
| 505 } else { | 476 } else { |
| 506 memcpy(src2, src1, 3 * w * sizeof(*src2)); | 477 memcpy(src2, src1, 3 * w * sizeof(*src2)); |
| 507 } | 478 } |
| 508 UpdateW(src1, target_y + (j + 0) * w, w); | 479 UpdateW(src1, target_y + (j + 0) * w, w); |
| 509 UpdateW(src2, target_y + (j + 1) * w, w); | 480 UpdateW(src2, target_y + (j + 1) * w, w); |
| 510 UpdateChroma(src1, src2, target_uv + uv_off, dst_y, uv_w); | 481 diff_sum += UpdateChroma(src1, src2, target_uv + uv_off, dst_y, uv_w); |
| 511 memcpy(best_uv + uv_off, target_uv + uv_off, 3 * uv_w * sizeof(*best_uv)); | 482 memcpy(best_uv + uv_off, target_uv + uv_off, 3 * uv_w * sizeof(*best_uv)); |
| 512 memcpy(dst_y + w, dst_y, w * sizeof(*dst_y)); | 483 memcpy(dst_y + w, dst_y, w * sizeof(*dst_y)); |
| 513 } | 484 } |
| 514 | 485 |
| 515 // Iterate and resolve clipping conflicts. | 486 // Iterate and resolve clipping conflicts. |
| 516 for (iter = 0; iter < kNumIterations; ++iter) { | 487 for (iter = 0; iter < kNumIterations; ++iter) { |
| 517 int k; | 488 int k; |
| 518 const fixed_t* cur_uv = best_uv; | 489 const fixed_t* cur_uv = best_uv; |
| 519 const fixed_t* prev_uv = best_uv; | 490 const fixed_t* prev_uv = best_uv; |
| 491 const int old_diff_sum = diff_sum; |
| 492 diff_sum = 0; |
| 520 for (j = 0; j < h; j += 2) { | 493 for (j = 0; j < h; j += 2) { |
| 521 fixed_y_t* const src1 = tmp_buffer; | 494 fixed_y_t* const src1 = tmp_buffer; |
| 522 fixed_y_t* const src2 = tmp_buffer + 3 * w; | 495 fixed_y_t* const src2 = tmp_buffer + 3 * w; |
| 523 | |
| 524 { | 496 { |
| 525 const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0); | 497 const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0); |
| 526 InterpolateTwoRows(best_y + j * w, prev_uv, cur_uv, next_uv, | 498 InterpolateTwoRows(best_y + j * w, prev_uv, cur_uv, next_uv, |
| 527 w, src1, src2); | 499 w, src1, src2); |
| 528 prev_uv = cur_uv; | 500 prev_uv = cur_uv; |
| 529 cur_uv = next_uv; | 501 cur_uv = next_uv; |
| 530 } | 502 } |
| 531 | 503 |
| 532 UpdateW(src1, best_rgb_y + 0 * w, w); | 504 UpdateW(src1, best_rgb_y + 0 * w, w); |
| 533 UpdateW(src2, best_rgb_y + 1 * w, w); | 505 UpdateW(src2, best_rgb_y + 1 * w, w); |
| 534 UpdateChroma(src1, src2, best_rgb_uv, NULL, uv_w); | 506 diff_sum += UpdateChroma(src1, src2, best_rgb_uv, NULL, uv_w); |
| 535 | 507 |
| 536 // update two rows of Y and one row of RGB | 508 // update two rows of Y and one row of RGB |
| 537 for (i = 0; i < 2 * w; ++i) { | 509 for (i = 0; i < 2 * w; ++i) { |
| 538 const int off = i + j * w; | 510 const int off = i + j * w; |
| 539 const int diff_y = target_y[off] - best_rgb_y[i]; | 511 const int diff_y = target_y[off] - best_rgb_y[i]; |
| 540 const int new_y = (int)best_y[off] + diff_y; | 512 const int new_y = (int)best_y[off] + diff_y; |
| 541 best_y[off] = clip_y(new_y); | 513 best_y[off] = clip_y(new_y); |
| 542 } | 514 } |
| 543 for (i = 0; i < uv_w; ++i) { | 515 for (i = 0; i < uv_w; ++i) { |
| 544 const int off = 3 * (i + (j >> 1) * uv_w); | 516 const int off = 3 * (i + (j >> 1) * uv_w); |
| 545 int W; | 517 int W; |
| 546 for (k = 0; k <= 2; ++k) { | 518 for (k = 0; k <= 2; ++k) { |
| 547 const int diff_uv = (int)target_uv[off + k] - best_rgb_uv[3 * i + k]; | 519 const int diff_uv = (int)target_uv[off + k] - best_rgb_uv[3 * i + k]; |
| 548 best_uv[off + k] += diff_uv; | 520 best_uv[off + k] += diff_uv; |
| 549 } | 521 } |
| 550 W = RGBToGray(best_uv[off + 0], best_uv[off + 1], best_uv[off + 2]); | 522 W = RGBToGray(best_uv[off + 0], best_uv[off + 1], best_uv[off + 2]); |
| 551 for (k = 0; k <= 2; ++k) { | 523 for (k = 0; k <= 2; ++k) { |
| 552 best_uv[off + k] -= W; | 524 best_uv[off + k] -= W; |
| 553 } | 525 } |
| 554 } | 526 } |
| 555 } | 527 } |
| 556 // TODO(skal): add early-termination criterion | 528 // test exit condition |
| 529 if (diff_sum > 0) { |
| 530 const int improvement = 100 * abs(diff_sum - old_diff_sum) / diff_sum; |
| 531 // Check if first iteration gave good result already, without a large |
| 532 // jump of improvement (otherwise it means we need to try few extra |
| 533 // iterations, just to be sure). |
| 534 if (iter == 0 && diff_sum < first_diff_threshold && |
| 535 improvement < min_first_improvement) { |
| 536 break; |
| 537 } |
| 538 // then, check if improvement is stalling. |
| 539 if (improvement < min_improvement) { |
| 540 break; |
| 541 } |
| 542 } else { |
| 543 break; |
| 544 } |
| 557 } | 545 } |
| 558 | 546 |
| 559 // final reconstruction | 547 // final reconstruction |
| 560 ok = ConvertWRGBToYUV(best_y, best_uv, picture); | 548 ok = ConvertWRGBToYUV(best_y, best_uv, picture); |
| 561 | 549 |
| 562 End: | 550 End: |
| 563 WebPSafeFree(best_y); | 551 WebPSafeFree(best_y); |
| 564 WebPSafeFree(best_uv); | 552 WebPSafeFree(best_uv); |
| 565 WebPSafeFree(target_y); | 553 WebPSafeFree(target_y); |
| 566 WebPSafeFree(target_uv); | 554 WebPSafeFree(target_uv); |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 755 } | 743 } |
| 756 | 744 |
| 757 static WEBP_INLINE void ConvertRowToY(const uint8_t* const r_ptr, | 745 static WEBP_INLINE void ConvertRowToY(const uint8_t* const r_ptr, |
| 758 const uint8_t* const g_ptr, | 746 const uint8_t* const g_ptr, |
| 759 const uint8_t* const b_ptr, | 747 const uint8_t* const b_ptr, |
| 760 int step, | 748 int step, |
| 761 uint8_t* const dst_y, | 749 uint8_t* const dst_y, |
| 762 int width, | 750 int width, |
| 763 VP8Random* const rg) { | 751 VP8Random* const rg) { |
| 764 int i, j; | 752 int i, j; |
| 765 for (i = 0, j = 0; i < width; ++i, j += step) { | 753 for (i = 0, j = 0; i < width; i += 1, j += step) { |
| 766 dst_y[i] = RGBToY(r_ptr[j], g_ptr[j], b_ptr[j], rg); | 754 dst_y[i] = RGBToY(r_ptr[j], g_ptr[j], b_ptr[j], rg); |
| 767 } | 755 } |
| 768 } | 756 } |
| 769 | 757 |
| 770 static WEBP_INLINE void ConvertRowsToUVWithAlpha(const uint8_t* const r_ptr, | 758 static WEBP_INLINE void AccumulateRGBA(const uint8_t* const r_ptr, |
| 771 const uint8_t* const g_ptr, | 759 const uint8_t* const g_ptr, |
| 772 const uint8_t* const b_ptr, | 760 const uint8_t* const b_ptr, |
| 773 const uint8_t* const a_ptr, | 761 const uint8_t* const a_ptr, |
| 774 int rgb_stride, | 762 int rgb_stride, |
| 775 uint8_t* const dst_u, | 763 uint16_t* dst, int width) { |
| 776 uint8_t* const dst_v, | |
| 777 int width, | |
| 778 VP8Random* const rg) { | |
| 779 int i, j; | 764 int i, j; |
| 780 // we loop over 2x2 blocks and produce one U/V value for each. | 765 // we loop over 2x2 blocks and produce one R/G/B/A value for each. |
| 781 for (i = 0, j = 0; i < (width >> 1); ++i, j += 2 * sizeof(uint32_t)) { | 766 for (i = 0, j = 0; i < (width >> 1); i += 1, j += 2 * 4, dst += 4) { |
| 782 const uint32_t a = SUM4ALPHA(a_ptr + j); | 767 const uint32_t a = SUM4ALPHA(a_ptr + j); |
| 783 int r, g, b; | 768 int r, g, b; |
| 784 if (a == 4 * 0xff || a == 0) { | 769 if (a == 4 * 0xff || a == 0) { |
| 785 r = SUM4(r_ptr + j, 4); | 770 r = SUM4(r_ptr + j, 4); |
| 786 g = SUM4(g_ptr + j, 4); | 771 g = SUM4(g_ptr + j, 4); |
| 787 b = SUM4(b_ptr + j, 4); | 772 b = SUM4(b_ptr + j, 4); |
| 788 } else { | 773 } else { |
| 789 r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 4, rgb_stride); | 774 r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 4, rgb_stride); |
| 790 g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 4, rgb_stride); | 775 g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 4, rgb_stride); |
| 791 b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 4, rgb_stride); | 776 b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 4, rgb_stride); |
| 792 } | 777 } |
| 793 dst_u[i] = RGBToU(r, g, b, rg); | 778 dst[0] = r; |
| 794 dst_v[i] = RGBToV(r, g, b, rg); | 779 dst[1] = g; |
| 780 dst[2] = b; |
| 781 dst[3] = a; |
| 795 } | 782 } |
| 796 if (width & 1) { | 783 if (width & 1) { |
| 797 const uint32_t a = 2u * SUM2ALPHA(a_ptr + j); | 784 const uint32_t a = 2u * SUM2ALPHA(a_ptr + j); |
| 798 int r, g, b; | 785 int r, g, b; |
| 799 if (a == 4 * 0xff || a == 0) { | 786 if (a == 4 * 0xff || a == 0) { |
| 800 r = SUM2(r_ptr + j); | 787 r = SUM2(r_ptr + j); |
| 801 g = SUM2(g_ptr + j); | 788 g = SUM2(g_ptr + j); |
| 802 b = SUM2(b_ptr + j); | 789 b = SUM2(b_ptr + j); |
| 803 } else { | 790 } else { |
| 804 r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 0, rgb_stride); | 791 r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 0, rgb_stride); |
| 805 g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 0, rgb_stride); | 792 g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 0, rgb_stride); |
| 806 b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 0, rgb_stride); | 793 b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 0, rgb_stride); |
| 807 } | 794 } |
| 808 dst_u[i] = RGBToU(r, g, b, rg); | 795 dst[0] = r; |
| 809 dst_v[i] = RGBToV(r, g, b, rg); | 796 dst[1] = g; |
| 797 dst[2] = b; |
| 798 dst[3] = a; |
| 810 } | 799 } |
| 811 } | 800 } |
| 812 | 801 |
| 813 static WEBP_INLINE void ConvertRowsToUV(const uint8_t* const r_ptr, | 802 static WEBP_INLINE void AccumulateRGB(const uint8_t* const r_ptr, |
| 814 const uint8_t* const g_ptr, | 803 const uint8_t* const g_ptr, |
| 815 const uint8_t* const b_ptr, | 804 const uint8_t* const b_ptr, |
| 816 int step, int rgb_stride, | 805 int step, int rgb_stride, |
| 806 uint16_t* dst, int width) { |
| 807 int i, j; |
| 808 for (i = 0, j = 0; i < (width >> 1); i += 1, j += 2 * step, dst += 4) { |
| 809 dst[0] = SUM4(r_ptr + j, step); |
| 810 dst[1] = SUM4(g_ptr + j, step); |
| 811 dst[2] = SUM4(b_ptr + j, step); |
| 812 } |
| 813 if (width & 1) { |
| 814 dst[0] = SUM2(r_ptr + j); |
| 815 dst[1] = SUM2(g_ptr + j); |
| 816 dst[2] = SUM2(b_ptr + j); |
| 817 } |
| 818 } |
| 819 |
| 820 static WEBP_INLINE void ConvertRowsToUV(const uint16_t* rgb, |
| 817 uint8_t* const dst_u, | 821 uint8_t* const dst_u, |
| 818 uint8_t* const dst_v, | 822 uint8_t* const dst_v, |
| 819 int width, | 823 int width, |
| 820 VP8Random* const rg) { | 824 VP8Random* const rg) { |
| 821 int i, j; | 825 int i; |
| 822 for (i = 0, j = 0; i < (width >> 1); ++i, j += 2 * step) { | 826 for (i = 0; i < width; i += 1, rgb += 4) { |
| 823 const int r = SUM4(r_ptr + j, step); | 827 const int r = rgb[0], g = rgb[1], b = rgb[2]; |
| 824 const int g = SUM4(g_ptr + j, step); | |
| 825 const int b = SUM4(b_ptr + j, step); | |
| 826 dst_u[i] = RGBToU(r, g, b, rg); | 828 dst_u[i] = RGBToU(r, g, b, rg); |
| 827 dst_v[i] = RGBToV(r, g, b, rg); | 829 dst_v[i] = RGBToV(r, g, b, rg); |
| 828 } | 830 } |
| 829 if (width & 1) { | |
| 830 const int r = SUM2(r_ptr + j); | |
| 831 const int g = SUM2(g_ptr + j); | |
| 832 const int b = SUM2(b_ptr + j); | |
| 833 dst_u[i] = RGBToU(r, g, b, rg); | |
| 834 dst_v[i] = RGBToV(r, g, b, rg); | |
| 835 } | |
| 836 } | 831 } |
| 837 | 832 |
| 838 static int ImportYUVAFromRGBA(const uint8_t* const r_ptr, | 833 static int ImportYUVAFromRGBA(const uint8_t* const r_ptr, |
| 839 const uint8_t* const g_ptr, | 834 const uint8_t* const g_ptr, |
| 840 const uint8_t* const b_ptr, | 835 const uint8_t* const b_ptr, |
| 841 const uint8_t* const a_ptr, | 836 const uint8_t* const a_ptr, |
| 842 int step, // bytes per pixel | 837 int step, // bytes per pixel |
| 843 int rgb_stride, // bytes per scanline | 838 int rgb_stride, // bytes per scanline |
| 844 float dithering, | 839 float dithering, |
| 845 int use_iterative_conversion, | 840 int use_iterative_conversion, |
| 846 WebPPicture* const picture) { | 841 WebPPicture* const picture) { |
| 847 int y; | 842 int y; |
| 848 const int width = picture->width; | 843 const int width = picture->width; |
| 849 const int height = picture->height; | 844 const int height = picture->height; |
| 850 const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride); | 845 const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride); |
| 846 const int is_rgb = (r_ptr < b_ptr); // otherwise it's bgr |
| 851 | 847 |
| 852 picture->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420; | 848 picture->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420; |
| 853 picture->use_argb = 0; | 849 picture->use_argb = 0; |
| 854 | 850 |
| 855 // disable smart conversion if source is too small (overkill). | 851 // disable smart conversion if source is too small (overkill). |
| 856 if (width < kMinDimensionIterativeConversion || | 852 if (width < kMinDimensionIterativeConversion || |
| 857 height < kMinDimensionIterativeConversion) { | 853 height < kMinDimensionIterativeConversion) { |
| 858 use_iterative_conversion = 0; | 854 use_iterative_conversion = 0; |
| 859 } | 855 } |
| 860 | 856 |
| 861 if (!WebPPictureAllocYUVA(picture, width, height)) { | 857 if (!WebPPictureAllocYUVA(picture, width, height)) { |
| 862 return 0; | 858 return 0; |
| 863 } | 859 } |
| 864 if (has_alpha) { | 860 if (has_alpha) { |
| 865 WebPInitAlphaProcessing(); | 861 WebPInitAlphaProcessing(); |
| 866 assert(step == 4); | 862 assert(step == 4); |
| 867 #if defined(USE_INVERSE_ALPHA_TABLE) | 863 #if defined(USE_GAMMA_COMPRESSION) && defined(USE_INVERSE_ALPHA_TABLE) |
| 868 assert(kAlphaFix + kGammaFix <= 31); | 864 assert(kAlphaFix + kGammaFix <= 31); |
| 869 #endif | 865 #endif |
| 870 } | 866 } |
| 871 | 867 |
| 872 if (use_iterative_conversion) { | 868 if (use_iterative_conversion) { |
| 873 InitGammaTablesF(); | 869 InitGammaTablesF(); |
| 874 if (!PreprocessARGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, picture)) { | 870 if (!PreprocessARGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, picture)) { |
| 875 return 0; | 871 return 0; |
| 876 } | 872 } |
| 877 if (has_alpha) { | 873 if (has_alpha) { |
| 878 WebPExtractAlpha(a_ptr, rgb_stride, width, height, | 874 WebPExtractAlpha(a_ptr, rgb_stride, width, height, |
| 879 picture->a, picture->a_stride); | 875 picture->a, picture->a_stride); |
| 880 } | 876 } |
| 881 } else { | 877 } else { |
| 878 const int uv_width = (width + 1) >> 1; |
| 879 int use_dsp = (step == 3); // use special function in this case |
| 880 // temporary storage for accumulated R/G/B values during conversion to U/V |
| 881 uint16_t* const tmp_rgb = |
| 882 (uint16_t*)WebPSafeMalloc(4 * uv_width, sizeof(*tmp_rgb)); |
| 882 uint8_t* dst_y = picture->y; | 883 uint8_t* dst_y = picture->y; |
| 883 uint8_t* dst_u = picture->u; | 884 uint8_t* dst_u = picture->u; |
| 884 uint8_t* dst_v = picture->v; | 885 uint8_t* dst_v = picture->v; |
| 885 uint8_t* dst_a = picture->a; | 886 uint8_t* dst_a = picture->a; |
| 886 | 887 |
| 887 VP8Random base_rg; | 888 VP8Random base_rg; |
| 888 VP8Random* rg = NULL; | 889 VP8Random* rg = NULL; |
| 889 if (dithering > 0.) { | 890 if (dithering > 0.) { |
| 890 VP8InitRandom(&base_rg, dithering); | 891 VP8InitRandom(&base_rg, dithering); |
| 891 rg = &base_rg; | 892 rg = &base_rg; |
| 893 use_dsp = 0; // can't use dsp in this case |
| 892 } | 894 } |
| 895 WebPInitConvertARGBToYUV(); |
| 896 InitGammaTables(); |
| 893 | 897 |
| 894 InitGammaTables(); | 898 if (tmp_rgb == NULL) return 0; // malloc error |
| 895 | 899 |
| 896 // Downsample Y/U/V planes, two rows at a time | 900 // Downsample Y/U/V planes, two rows at a time |
| 897 for (y = 0; y < (height >> 1); ++y) { | 901 for (y = 0; y < (height >> 1); ++y) { |
| 898 int rows_have_alpha = has_alpha; | 902 int rows_have_alpha = has_alpha; |
| 899 const int off1 = (2 * y + 0) * rgb_stride; | 903 const int off1 = (2 * y + 0) * rgb_stride; |
| 900 const int off2 = (2 * y + 1) * rgb_stride; | 904 const int off2 = (2 * y + 1) * rgb_stride; |
| 901 ConvertRowToY(r_ptr + off1, g_ptr + off1, b_ptr + off1, step, | 905 if (use_dsp) { |
| 902 dst_y, width, rg); | 906 if (is_rgb) { |
| 903 ConvertRowToY(r_ptr + off2, g_ptr + off2, b_ptr + off2, step, | 907 WebPConvertRGB24ToY(r_ptr + off1, dst_y, width); |
| 904 dst_y + picture->y_stride, width, rg); | 908 WebPConvertRGB24ToY(r_ptr + off2, dst_y + picture->y_stride, width); |
| 909 } else { |
| 910 WebPConvertBGR24ToY(b_ptr + off1, dst_y, width); |
| 911 WebPConvertBGR24ToY(b_ptr + off2, dst_y + picture->y_stride, width); |
| 912 } |
| 913 } else { |
| 914 ConvertRowToY(r_ptr + off1, g_ptr + off1, b_ptr + off1, step, |
| 915 dst_y, width, rg); |
| 916 ConvertRowToY(r_ptr + off2, g_ptr + off2, b_ptr + off2, step, |
| 917 dst_y + picture->y_stride, width, rg); |
| 918 } |
| 905 dst_y += 2 * picture->y_stride; | 919 dst_y += 2 * picture->y_stride; |
| 906 if (has_alpha) { | 920 if (has_alpha) { |
| 907 rows_have_alpha &= !WebPExtractAlpha(a_ptr + off1, rgb_stride, | 921 rows_have_alpha &= !WebPExtractAlpha(a_ptr + off1, rgb_stride, |
| 908 width, 2, | 922 width, 2, |
| 909 dst_a, picture->a_stride); | 923 dst_a, picture->a_stride); |
| 910 dst_a += 2 * picture->a_stride; | 924 dst_a += 2 * picture->a_stride; |
| 911 } | 925 } |
| 926 // Collect averaged R/G/B(/A) |
| 912 if (!rows_have_alpha) { | 927 if (!rows_have_alpha) { |
| 913 ConvertRowsToUV(r_ptr + off1, g_ptr + off1, b_ptr + off1, | 928 AccumulateRGB(r_ptr + off1, g_ptr + off1, b_ptr + off1, |
| 914 step, rgb_stride, dst_u, dst_v, width, rg); | 929 step, rgb_stride, tmp_rgb, width); |
| 915 } else { | 930 } else { |
| 916 ConvertRowsToUVWithAlpha(r_ptr + off1, g_ptr + off1, b_ptr + off1, | 931 AccumulateRGBA(r_ptr + off1, g_ptr + off1, b_ptr + off1, a_ptr + off1, |
| 917 a_ptr + off1, rgb_stride, | 932 rgb_stride, tmp_rgb, width); |
| 918 dst_u, dst_v, width, rg); | 933 } |
| 934 // Convert to U/V |
| 935 if (rg == NULL) { |
| 936 WebPConvertRGBA32ToUV(tmp_rgb, dst_u, dst_v, uv_width); |
| 937 } else { |
| 938 ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg); |
| 919 } | 939 } |
| 920 dst_u += picture->uv_stride; | 940 dst_u += picture->uv_stride; |
| 921 dst_v += picture->uv_stride; | 941 dst_v += picture->uv_stride; |
| 922 } | 942 } |
| 923 if (height & 1) { // extra last row | 943 if (height & 1) { // extra last row |
| 924 const int off = 2 * y * rgb_stride; | 944 const int off = 2 * y * rgb_stride; |
| 925 int row_has_alpha = has_alpha; | 945 int row_has_alpha = has_alpha; |
| 926 ConvertRowToY(r_ptr + off, g_ptr + off, b_ptr + off, step, | 946 if (use_dsp) { |
| 927 dst_y, width, rg); | 947 if (r_ptr < b_ptr) { |
| 948 WebPConvertRGB24ToY(r_ptr + off, dst_y, width); |
| 949 } else { |
| 950 WebPConvertBGR24ToY(b_ptr + off, dst_y, width); |
| 951 } |
| 952 } else { |
| 953 ConvertRowToY(r_ptr + off, g_ptr + off, b_ptr + off, step, |
| 954 dst_y, width, rg); |
| 955 } |
| 928 if (row_has_alpha) { | 956 if (row_has_alpha) { |
| 929 row_has_alpha &= !WebPExtractAlpha(a_ptr + off, 0, width, 1, dst_a, 0); | 957 row_has_alpha &= !WebPExtractAlpha(a_ptr + off, 0, width, 1, dst_a, 0); |
| 930 } | 958 } |
| 959 // Collect averaged R/G/B(/A) |
| 931 if (!row_has_alpha) { | 960 if (!row_has_alpha) { |
| 932 ConvertRowsToUV(r_ptr + off, g_ptr + off, b_ptr + off, | 961 // Collect averaged R/G/B |
| 933 step, 0, dst_u, dst_v, width, rg); | 962 AccumulateRGB(r_ptr + off, g_ptr + off, b_ptr + off, |
| 963 step, /* rgb_stride = */ 0, tmp_rgb, width); |
| 934 } else { | 964 } else { |
| 935 ConvertRowsToUVWithAlpha(r_ptr + off, g_ptr + off, b_ptr + off, | 965 AccumulateRGBA(r_ptr + off, g_ptr + off, b_ptr + off, a_ptr + off, |
| 936 a_ptr + off, 0, | 966 /* rgb_stride = */ 0, tmp_rgb, width); |
| 937 dst_u, dst_v, width, rg); | 967 } |
| 968 if (rg == NULL) { |
| 969 WebPConvertRGBA32ToUV(tmp_rgb, dst_u, dst_v, uv_width); |
| 970 } else { |
| 971 ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg); |
| 938 } | 972 } |
| 939 } | 973 } |
| 974 WebPSafeFree(tmp_rgb); |
| 940 } | 975 } |
| 941 return 1; | 976 return 1; |
| 942 } | 977 } |
| 943 | 978 |
| 944 #undef SUM4 | 979 #undef SUM4 |
| 945 #undef SUM2 | 980 #undef SUM2 |
| 946 #undef SUM4ALPHA | 981 #undef SUM4ALPHA |
| 947 #undef SUM2ALPHA | 982 #undef SUM2ALPHA |
| 948 | 983 |
| 949 //------------------------------------------------------------------------------ | 984 //------------------------------------------------------------------------------ |
| (...skipping 21 matching lines...) Expand all Loading... |
| 971 | 1006 |
| 972 int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace, | 1007 int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace, |
| 973 float dithering) { | 1008 float dithering) { |
| 974 return PictureARGBToYUVA(picture, colorspace, dithering, 0); | 1009 return PictureARGBToYUVA(picture, colorspace, dithering, 0); |
| 975 } | 1010 } |
| 976 | 1011 |
| 977 int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) { | 1012 int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) { |
| 978 return PictureARGBToYUVA(picture, colorspace, 0.f, 0); | 1013 return PictureARGBToYUVA(picture, colorspace, 0.f, 0); |
| 979 } | 1014 } |
| 980 | 1015 |
| 981 #if WEBP_ENCODER_ABI_VERSION > 0x0204 | |
| 982 int WebPPictureSmartARGBToYUVA(WebPPicture* picture) { | 1016 int WebPPictureSmartARGBToYUVA(WebPPicture* picture) { |
| 983 return PictureARGBToYUVA(picture, WEBP_YUV420, 0.f, 1); | 1017 return PictureARGBToYUVA(picture, WEBP_YUV420, 0.f, 1); |
| 984 } | 1018 } |
| 985 #endif | |
| 986 | 1019 |
| 987 //------------------------------------------------------------------------------ | 1020 //------------------------------------------------------------------------------ |
| 988 // call for YUVA -> ARGB conversion | 1021 // call for YUVA -> ARGB conversion |
| 989 | 1022 |
| 990 int WebPPictureYUVAToARGB(WebPPicture* picture) { | 1023 int WebPPictureYUVAToARGB(WebPPicture* picture) { |
| 991 if (picture == NULL) return 0; | 1024 if (picture == NULL) return 0; |
| 992 if (picture->y == NULL || picture->u == NULL || picture->v == NULL) { | 1025 if (picture->y == NULL || picture->u == NULL || picture->v == NULL) { |
| 993 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); | 1026 return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); |
| 994 } | 1027 } |
| 995 if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) { | 1028 if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1059 const uint8_t* const a_ptr = import_alpha ? rgb + 3 : NULL; | 1092 const uint8_t* const a_ptr = import_alpha ? rgb + 3 : NULL; |
| 1060 const int width = picture->width; | 1093 const int width = picture->width; |
| 1061 const int height = picture->height; | 1094 const int height = picture->height; |
| 1062 | 1095 |
| 1063 if (!picture->use_argb) { | 1096 if (!picture->use_argb) { |
| 1064 return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride, | 1097 return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride, |
| 1065 0.f /* no dithering */, 0, picture); | 1098 0.f /* no dithering */, 0, picture); |
| 1066 } | 1099 } |
| 1067 if (!WebPPictureAlloc(picture)) return 0; | 1100 if (!WebPPictureAlloc(picture)) return 0; |
| 1068 | 1101 |
| 1069 assert(step >= (import_alpha ? 4 : 3)); | 1102 VP8EncDspARGBInit(); |
| 1070 for (y = 0; y < height; ++y) { | 1103 |
| 1071 uint32_t* const dst = &picture->argb[y * picture->argb_stride]; | 1104 if (import_alpha) { |
| 1072 int x; | 1105 assert(step == 4); |
| 1073 for (x = 0; x < width; ++x) { | 1106 for (y = 0; y < height; ++y) { |
| 1074 const int offset = step * x + y * rgb_stride; | 1107 uint32_t* const dst = &picture->argb[y * picture->argb_stride]; |
| 1075 dst[x] = MakeARGB32(import_alpha ? a_ptr[offset] : 0xff, | 1108 const int offset = y * rgb_stride; |
| 1076 r_ptr[offset], g_ptr[offset], b_ptr[offset]); | 1109 VP8PackARGB(a_ptr + offset, r_ptr + offset, g_ptr + offset, |
| 1110 b_ptr + offset, width, dst); |
| 1111 } |
| 1112 } else { |
| 1113 assert(step >= 3); |
| 1114 for (y = 0; y < height; ++y) { |
| 1115 uint32_t* const dst = &picture->argb[y * picture->argb_stride]; |
| 1116 const int offset = y * rgb_stride; |
| 1117 VP8PackRGB(r_ptr + offset, g_ptr + offset, b_ptr + offset, |
| 1118 width, step, dst); |
| 1077 } | 1119 } |
| 1078 } | 1120 } |
| 1079 return 1; | 1121 return 1; |
| 1080 } | 1122 } |
| 1081 | 1123 |
| 1082 // Public API | 1124 // Public API |
| 1083 | 1125 |
| 1084 int WebPPictureImportRGB(WebPPicture* picture, | 1126 int WebPPictureImportRGB(WebPPicture* picture, |
| 1085 const uint8_t* rgb, int rgb_stride) { | 1127 const uint8_t* rgb, int rgb_stride) { |
| 1086 return (picture != NULL) ? Import(picture, rgb, rgb_stride, 3, 0, 0) : 0; | 1128 return (picture != NULL) ? Import(picture, rgb, rgb_stride, 3, 0, 0) : 0; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1105 const uint8_t* rgba, int rgba_stride) { | 1147 const uint8_t* rgba, int rgba_stride) { |
| 1106 return (picture != NULL) ? Import(picture, rgba, rgba_stride, 4, 0, 0) : 0; | 1148 return (picture != NULL) ? Import(picture, rgba, rgba_stride, 4, 0, 0) : 0; |
| 1107 } | 1149 } |
| 1108 | 1150 |
| 1109 int WebPPictureImportBGRX(WebPPicture* picture, | 1151 int WebPPictureImportBGRX(WebPPicture* picture, |
| 1110 const uint8_t* rgba, int rgba_stride) { | 1152 const uint8_t* rgba, int rgba_stride) { |
| 1111 return (picture != NULL) ? Import(picture, rgba, rgba_stride, 4, 1, 0) : 0; | 1153 return (picture != NULL) ? Import(picture, rgba, rgba_stride, 4, 1, 0) : 0; |
| 1112 } | 1154 } |
| 1113 | 1155 |
| 1114 //------------------------------------------------------------------------------ | 1156 //------------------------------------------------------------------------------ |
| OLD | NEW |