Skip to content

Commit 5ecee06

Browse files
maryla-ucGerrit Code Review
authored andcommitted
Merge "sharpyuv: increase precision of gamma<->linear conversion" into main
2 parents f81dd7d + 2d607ee commit 5ecee06

File tree

8 files changed

+173
-120
lines changed

8 files changed

+173
-120
lines changed

Android.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ sharpyuv_srcs := \
3737
sharpyuv/sharpyuv.c \
3838
sharpyuv/sharpyuv_csp.c \
3939
sharpyuv/sharpyuv_dsp.c \
40+
sharpyuv/sharpyuv_gamma.c \
4041
sharpyuv/sharpyuv_neon.$(NEON) \
4142
sharpyuv/sharpyuv_sse2.c \
4243

Makefile.vc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ SHARPYUV_OBJS = \
178178
$(DIROBJ)\sharpyuv\sharpyuv.obj \
179179
$(DIROBJ)\sharpyuv\sharpyuv_csp.obj \
180180
$(DIROBJ)\sharpyuv\sharpyuv_dsp.obj \
181+
$(DIROBJ)\sharpyuv\sharpyuv_gamma.obj \
181182
$(DIROBJ)\sharpyuv\sharpyuv_neon.obj \
182183
$(DIROBJ)\sharpyuv\sharpyuv_sse2.obj \
183184

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ model {
109109
include "sharpyuv.c"
110110
include "sharpyuv_csp.c"
111111
include "sharpyuv_dsp.c"
112+
include "sharpyuv_gamma.c"
112113
include "sharpyuv_neon.c"
113114
include "sharpyuv_sse2.c"
114115
srcDir "src/dec"

makefile.unix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ SHARPYUV_OBJS = \
129129
sharpyuv/sharpyuv.o \
130130
sharpyuv/sharpyuv_csp.o \
131131
sharpyuv/sharpyuv_dsp.o \
132+
sharpyuv/sharpyuv_gamma.o \
132133
sharpyuv/sharpyuv_neon.o \
133134
sharpyuv/sharpyuv_sse2.o \
134135

sharpyuv/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ libsharpyuv_neon_la_CFLAGS = $(AM_CFLAGS) $(NEON_FLAGS)
2222
libsharpyuv_la_SOURCES =
2323
libsharpyuv_la_SOURCES += sharpyuv_csp.c sharpyuv_csp.h
2424
libsharpyuv_la_SOURCES += sharpyuv_dsp.c sharpyuv_dsp.h
25+
libsharpyuv_la_SOURCES += sharpyuv_gamma.c sharpyuv_gamma.h
2526
libsharpyuv_la_SOURCES += sharpyuv.c sharpyuv.h
2627

2728
libsharpyuv_la_CPPFLAGS = $(AM_CPPFLAGS)

sharpyuv/sharpyuv.c

Lines changed: 19 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "src/webp/types.h"
2222
#include "src/dsp/cpu.h"
2323
#include "sharpyuv/sharpyuv_dsp.h"
24+
#include "sharpyuv/sharpyuv_gamma.h"
2425

2526
//------------------------------------------------------------------------------
2627
// Sharp RGB->YUV conversion
@@ -45,100 +46,6 @@ static int GetPrecisionShift(int rgb_bit_depth) {
4546
typedef int16_t fixed_t; // signed type with extra precision for UV
4647
typedef uint16_t fixed_y_t; // unsigned type with extra precision for W
4748

48-
//------------------------------------------------------------------------------
49-
// Code for gamma correction
50-
51-
// Gamma correction compensates loss of resolution during chroma subsampling.
52-
// Size of pre-computed table for converting from gamma to linear.
53-
#define GAMMA_TO_LINEAR_TAB_BITS 10
54-
#define GAMMA_TO_LINEAR_TAB_SIZE (1 << GAMMA_TO_LINEAR_TAB_BITS)
55-
static uint32_t kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 2];
56-
// Size of pre-computed table for converting from linear to gamma.
57-
#define LINEAR_TO_GAMMA_TAB_BITS 8
58-
#define LINEAR_TO_GAMMA_TAB_SIZE (1 << LINEAR_TO_GAMMA_TAB_BITS)
59-
static uint32_t kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 2];
60-
61-
static const double kGammaF = 1. / 0.45;
62-
#define GAMMA_TO_LINEAR_BITS 14
63-
64-
static volatile int kGammaTablesSOk = 0;
65-
static void InitGammaTablesS(void) {
66-
assert(2 * GAMMA_TO_LINEAR_BITS < 32); // we use uint32_t intermediate values
67-
if (!kGammaTablesSOk) {
68-
int v;
69-
const double a = 0.09929682680944;
70-
const double thresh = 0.018053968510807;
71-
// Precompute gamma to linear table.
72-
{
73-
const double norm = 1. / GAMMA_TO_LINEAR_TAB_SIZE;
74-
const double a_rec = 1. / (1. + a);
75-
const double final_scale = 1 << GAMMA_TO_LINEAR_BITS;
76-
for (v = 0; v <= GAMMA_TO_LINEAR_TAB_SIZE; ++v) {
77-
const double g = norm * v;
78-
double value;
79-
if (g <= thresh * 4.5) {
80-
value = g / 4.5;
81-
} else {
82-
value = pow(a_rec * (g + a), kGammaF);
83-
}
84-
kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5);
85-
}
86-
// to prevent small rounding errors to cause read-overflow:
87-
kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 1] =
88-
kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE];
89-
}
90-
// Precompute linear to gamma table.
91-
{
92-
const double scale = 1. / LINEAR_TO_GAMMA_TAB_SIZE;
93-
for (v = 0; v <= LINEAR_TO_GAMMA_TAB_SIZE; ++v) {
94-
const double g = scale * v;
95-
double value;
96-
if (g <= thresh) {
97-
value = 4.5 * g;
98-
} else {
99-
value = (1. + a) * pow(g, 1. / kGammaF) - a;
100-
}
101-
kLinearToGammaTabS[v] =
102-
(uint32_t)(GAMMA_TO_LINEAR_TAB_SIZE * value + 0.5);
103-
}
104-
// to prevent small rounding errors to cause read-overflow:
105-
kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 1] =
106-
kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE];
107-
}
108-
kGammaTablesSOk = 1;
109-
}
110-
}
111-
112-
static WEBP_INLINE uint32_t FixedPointInterpolation(int v, uint32_t* tab,
113-
int tab_pos_shift,
114-
int tab_value_shift) {
115-
const uint32_t tab_pos = v >> tab_pos_shift;
116-
// fractional part, in 'tab_pos_shift' fixed-point precision
117-
const uint32_t x = v - (tab_pos << tab_pos_shift); // fractional part
118-
// v0 / v1 are in kGammaToLinearBits fixed-point precision (range [0..1])
119-
const uint32_t v0 = tab[tab_pos + 0] << tab_value_shift;
120-
const uint32_t v1 = tab[tab_pos + 1] << tab_value_shift;
121-
// Final interpolation.
122-
const uint32_t v2 = (v1 - v0) * x; // note: v1 >= v0.
123-
const int half = (tab_pos_shift > 0) ? 1 << (tab_pos_shift - 1) : 0;
124-
const uint32_t result = v0 + ((v2 + half) >> tab_pos_shift);
125-
return result;
126-
}
127-
128-
static WEBP_INLINE uint32_t GammaToLinear(int v, int bit_depth) {
129-
const int shift = GAMMA_TO_LINEAR_TAB_BITS - bit_depth;
130-
if (shift > 0) {
131-
return kGammaToLinearTabS[v << shift];
132-
}
133-
return FixedPointInterpolation(v, kGammaToLinearTabS, -shift, 0);
134-
}
135-
136-
static WEBP_INLINE uint32_t LinearToGamma(uint32_t value, int bit_depth) {
137-
const uint32_t v = value << LINEAR_TO_GAMMA_TAB_BITS;
138-
return FixedPointInterpolation(v, kLinearToGammaTabS, GAMMA_TO_LINEAR_BITS,
139-
bit_depth - GAMMA_TO_LINEAR_TAB_BITS);
140-
}
141-
14249
//------------------------------------------------------------------------------
14350

14451
static uint8_t clip_8b(fixed_t v) {
@@ -161,25 +68,26 @@ static int RGBToGray(int64_t r, int64_t g, int64_t b) {
16168
return (int)(luma >> YUV_FIX);
16269
}
16370

164-
static uint32_t ScaleDown(int a, int b, int c, int d, int rgb_bit_depth) {
71+
static uint32_t ScaleDown(uint16_t a, uint16_t b, uint16_t c, uint16_t d,
72+
int rgb_bit_depth) {
16573
const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
166-
const uint32_t A = GammaToLinear(a, bit_depth);
167-
const uint32_t B = GammaToLinear(b, bit_depth);
168-
const uint32_t C = GammaToLinear(c, bit_depth);
169-
const uint32_t D = GammaToLinear(d, bit_depth);
170-
return LinearToGamma((A + B + C + D + 2) >> 2, bit_depth);
74+
const uint32_t A = SharpYuvGammaToLinear(a, bit_depth);
75+
const uint32_t B = SharpYuvGammaToLinear(b, bit_depth);
76+
const uint32_t C = SharpYuvGammaToLinear(c, bit_depth);
77+
const uint32_t D = SharpYuvGammaToLinear(d, bit_depth);
78+
return SharpYuvLinearToGamma((A + B + C + D + 2) >> 2, bit_depth);
17179
}
17280

17381
static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w,
17482
int rgb_bit_depth) {
17583
const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
17684
int i;
17785
for (i = 0; i < w; ++i) {
178-
const uint32_t R = GammaToLinear(src[0 * w + i], bit_depth);
179-
const uint32_t G = GammaToLinear(src[1 * w + i], bit_depth);
180-
const uint32_t B = GammaToLinear(src[2 * w + i], bit_depth);
86+
const uint32_t R = SharpYuvGammaToLinear(src[0 * w + i], bit_depth);
87+
const uint32_t G = SharpYuvGammaToLinear(src[1 * w + i], bit_depth);
88+
const uint32_t B = SharpYuvGammaToLinear(src[2 * w + i], bit_depth);
18189
const uint32_t Y = RGBToGray(R, G, B);
182-
dst[i] = (fixed_y_t)LinearToGamma(Y, bit_depth);
90+
dst[i] = (fixed_y_t)SharpYuvLinearToGamma(Y, bit_depth);
18391
}
18492
}
18593

@@ -227,15 +135,6 @@ static WEBP_INLINE int Shift(int v, int shift) {
227135
return (shift >= 0) ? (v << shift) : (v >> -shift);
228136
}
229137

230-
static WEBP_INLINE fixed_y_t ChangePrecision(uint16_t a, int shift) {
231-
if (shift == 0) return a;
232-
if (shift < 0) {
233-
const int rounding = 1 << (-shift - 1);
234-
return (a + rounding) >> -shift;
235-
}
236-
return ((fixed_y_t)a << shift);
237-
}
238-
239138
static void ImportOneRow(const uint8_t* const r_ptr,
240139
const uint8_t* const g_ptr,
241140
const uint8_t* const b_ptr,
@@ -252,13 +151,13 @@ static void ImportOneRow(const uint8_t* const r_ptr,
252151
const int off = i * step;
253152
const int shift = GetPrecisionShift(rgb_bit_depth);
254153
if (rgb_bit_depth == 8) {
255-
dst[i + 0 * w] = ChangePrecision(r_ptr[off], shift);
256-
dst[i + 1 * w] = ChangePrecision(g_ptr[off], shift);
257-
dst[i + 2 * w] = ChangePrecision(b_ptr[off], shift);
154+
dst[i + 0 * w] = Shift(r_ptr[off], shift);
155+
dst[i + 1 * w] = Shift(g_ptr[off], shift);
156+
dst[i + 2 * w] = Shift(b_ptr[off], shift);
258157
} else {
259-
dst[i + 0 * w] = ChangePrecision(((uint16_t*)r_ptr)[off], shift);
260-
dst[i + 1 * w] = ChangePrecision(((uint16_t*)g_ptr)[off], shift);
261-
dst[i + 2 * w] = ChangePrecision(((uint16_t*)b_ptr)[off], shift);
158+
dst[i + 0 * w] = Shift(((uint16_t*)r_ptr)[off], shift);
159+
dst[i + 1 * w] = Shift(((uint16_t*)g_ptr)[off], shift);
160+
dst[i + 2 * w] = Shift(((uint16_t*)b_ptr)[off], shift);
262161
}
263162
}
264163
if (pic_width & 1) { // replicate rightmost pixel
@@ -527,7 +426,7 @@ void SharpYuvInit(VP8CPUInfo cpu_info_func) {
527426

528427
SharpYuvInitDsp(cpu_info_func);
529428
if (!initialized) {
530-
InitGammaTablesS();
429+
SharpYuvInitGammaTables();
531430
}
532431

533432
sharpyuv_last_cpuinfo_used = cpu_info_func;

sharpyuv/sharpyuv_gamma.c

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright 2022 Google Inc. All Rights Reserved.
2+
//
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
5+
// tree. An additional intellectual property rights grant can be found
6+
// in the file PATENTS. All contributing project authors may
7+
// be found in the AUTHORS file in the root of the source tree.
8+
// -----------------------------------------------------------------------------
9+
//
10+
// Gamma correction utilities.
11+
12+
#include "sharpyuv/sharpyuv_gamma.h"
13+
14+
#include <assert.h>
15+
#include <math.h>
16+
#include <stdint.h>
17+
18+
#include "src/webp/types.h"
19+
20+
// Gamma correction compensates loss of resolution during chroma subsampling.
21+
// Size of pre-computed table for converting from gamma to linear.
22+
#define GAMMA_TO_LINEAR_TAB_BITS 10
23+
#define GAMMA_TO_LINEAR_TAB_SIZE (1 << GAMMA_TO_LINEAR_TAB_BITS)
24+
static uint32_t kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 2];
25+
#define LINEAR_TO_GAMMA_TAB_BITS 9
26+
#define LINEAR_TO_GAMMA_TAB_SIZE (1 << LINEAR_TO_GAMMA_TAB_BITS)
27+
static uint32_t kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 2];
28+
29+
static const double kGammaF = 1. / 0.45;
30+
#define GAMMA_TO_LINEAR_BITS 16
31+
32+
static volatile int kGammaTablesSOk = 0;
33+
void SharpYuvInitGammaTables(void) {
34+
assert(GAMMA_TO_LINEAR_BITS <= 16);
35+
if (!kGammaTablesSOk) {
36+
int v;
37+
const double a = 0.09929682680944;
38+
const double thresh = 0.018053968510807;
39+
const double final_scale = 1 << GAMMA_TO_LINEAR_BITS;
40+
// Precompute gamma to linear table.
41+
{
42+
const double norm = 1. / GAMMA_TO_LINEAR_TAB_SIZE;
43+
const double a_rec = 1. / (1. + a);
44+
for (v = 0; v <= GAMMA_TO_LINEAR_TAB_SIZE; ++v) {
45+
const double g = norm * v;
46+
double value;
47+
if (g <= thresh * 4.5) {
48+
value = g / 4.5;
49+
} else {
50+
value = pow(a_rec * (g + a), kGammaF);
51+
}
52+
kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5);
53+
}
54+
// to prevent small rounding errors to cause read-overflow:
55+
kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 1] =
56+
kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE];
57+
}
58+
// Precompute linear to gamma table.
59+
{
60+
const double scale = 1. / LINEAR_TO_GAMMA_TAB_SIZE;
61+
for (v = 0; v <= LINEAR_TO_GAMMA_TAB_SIZE; ++v) {
62+
const double g = scale * v;
63+
double value;
64+
if (g <= thresh) {
65+
value = 4.5 * g;
66+
} else {
67+
value = (1. + a) * pow(g, 1. / kGammaF) - a;
68+
}
69+
kLinearToGammaTabS[v] =
70+
(uint32_t)(final_scale * value + 0.5);
71+
}
72+
// to prevent small rounding errors to cause read-overflow:
73+
kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 1] =
74+
kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE];
75+
}
76+
kGammaTablesSOk = 1;
77+
}
78+
}
79+
80+
static WEBP_INLINE int Shift(int v, int shift) {
81+
return (shift >= 0) ? (v << shift) : (v >> -shift);
82+
}
83+
84+
static WEBP_INLINE uint32_t FixedPointInterpolation(int v, uint32_t* tab,
85+
int tab_pos_shift_right,
86+
int tab_value_shift) {
87+
const uint32_t tab_pos = Shift(v, -tab_pos_shift_right);
88+
// fractional part, in 'tab_pos_shift' fixed-point precision
89+
const uint32_t x = v - (tab_pos << tab_pos_shift_right); // fractional part
90+
// v0 / v1 are in kGammaToLinearBits fixed-point precision (range [0..1])
91+
const uint32_t v0 = Shift(tab[tab_pos + 0], tab_value_shift);
92+
const uint32_t v1 = Shift(tab[tab_pos + 1], tab_value_shift);
93+
// Final interpolation.
94+
const uint32_t v2 = (v1 - v0) * x; // note: v1 >= v0.
95+
const int half =
96+
(tab_pos_shift_right > 0) ? 1 << (tab_pos_shift_right - 1) : 0;
97+
const uint32_t result = v0 + ((v2 + half) >> tab_pos_shift_right);
98+
return result;
99+
}
100+
101+
uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth) {
102+
const int shift = GAMMA_TO_LINEAR_TAB_BITS - bit_depth;
103+
if (shift > 0) {
104+
return kGammaToLinearTabS[v << shift];
105+
}
106+
return FixedPointInterpolation(v, kGammaToLinearTabS, -shift, 0);
107+
}
108+
109+
uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth) {
110+
return FixedPointInterpolation(
111+
value, kLinearToGammaTabS,
112+
(GAMMA_TO_LINEAR_BITS - LINEAR_TO_GAMMA_TAB_BITS),
113+
bit_depth - GAMMA_TO_LINEAR_BITS);
114+
}

sharpyuv/sharpyuv_gamma.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2022 Google Inc. All Rights Reserved.
2+
//
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
5+
// tree. An additional intellectual property rights grant can be found
6+
// in the file PATENTS. All contributing project authors may
7+
// be found in the AUTHORS file in the root of the source tree.
8+
// -----------------------------------------------------------------------------
9+
//
10+
// Gamma correction utilities.
11+
12+
#ifndef WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
13+
#define WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
14+
15+
#include <stdint.h>
16+
17+
#ifdef __cplusplus
18+
extern "C" {
19+
#endif
20+
21+
// Initializes precomputed tables. Must be called once before calling
22+
// SharpYuvGammaToLinear or SharpYuvLinearToGamma.
23+
void SharpYuvInitGammaTables(void);
24+
25+
// Converts a gamma color value on 'bit_depth' bits to a 16 bit linear value.
26+
uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth);
27+
28+
// Converts a 16 bit linear color value to a gamma value on 'bit_depth' bits.
29+
uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth);
30+
31+
#ifdef __cplusplus
32+
} // extern "C"
33+
#endif
34+
35+
#endif // WEBP_SHARPYUV_SHARPYUV_GAMMA_H_

0 commit comments

Comments
 (0)