• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Eyevinn / hi264 / 22282734390

22 Feb 2026 06:23PM UTC coverage: 87.661% (+0.03%) from 87.632%
22282734390

push

github

tobbee
chore: update to Go v1.24 and run new go fix from Go v1.26

187 of 192 new or added lines in 16 files covered. (97.4%)

6614 of 7545 relevant lines covered (87.66%)

1.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.53
/pkg/encode/forward.go
1
package encode
2

3
import "github.com/Eyevinn/hi264/internal/transform"
4

5
// ForwardHadamard4x4 performs the forward 4x4 Hadamard transform for luma DC.
6
// The Hadamard transform is self-inverse (up to scaling), so we use the same
7
// butterfly structure as the inverse.
8
func ForwardHadamard4x4(dc [16]int32) [16]int32 {
1✔
9
        var temp [16]int32
1✔
10

1✔
11
        // 1D Hadamard on rows
1✔
12
        for i := range 4 {
2✔
13
                s0 := dc[i*4+0]
1✔
14
                s1 := dc[i*4+1]
1✔
15
                s2 := dc[i*4+2]
1✔
16
                s3 := dc[i*4+3]
1✔
17

1✔
18
                temp[i*4+0] = s0 + s1 + s2 + s3
1✔
19
                temp[i*4+1] = s0 + s1 - s2 - s3
1✔
20
                temp[i*4+2] = s0 - s1 - s2 + s3
1✔
21
                temp[i*4+3] = s0 - s1 + s2 - s3
1✔
22
        }
1✔
23

24
        // 1D Hadamard on columns, with /2 normalization
25
        var result [16]int32
1✔
26
        for j := range 4 {
2✔
27
                f0 := temp[0*4+j]
1✔
28
                f1 := temp[1*4+j]
1✔
29
                f2 := temp[2*4+j]
1✔
30
                f3 := temp[3*4+j]
1✔
31

1✔
32
                result[0*4+j] = (f0 + f1 + f2 + f3) / 2
1✔
33
                result[1*4+j] = (f0 + f1 - f2 - f3) / 2
1✔
34
                result[2*4+j] = (f0 - f1 - f2 + f3) / 2
1✔
35
                result[3*4+j] = (f0 - f1 + f2 - f3) / 2
1✔
36
        }
1✔
37

38
        return result
1✔
39
}
40

41
// ForwardHadamard2x2 performs the forward 2x2 Hadamard transform for chroma DC.
42
// Self-inverse (up to scaling).
43
func ForwardHadamard2x2(dc [4]int32) [4]int32 {
1✔
44
        return [4]int32{
1✔
45
                dc[0] + dc[1] + dc[2] + dc[3],
1✔
46
                dc[0] - dc[1] + dc[2] - dc[3],
1✔
47
                dc[0] + dc[1] - dc[2] - dc[3],
1✔
48
                dc[0] - dc[1] - dc[2] + dc[3],
1✔
49
        }
1✔
50
}
1✔
51

52
// MF4x4 is the forward quantization multiplication factor table.
53
// Indexed by QP%6 and position category (same as dequant LevelScale).
54
// These are the standard forward quantization factors from JM reference.
55
var MF4x4 = [6][3]int32{
56
        {13107, 8066, 5243},
57
        {11916, 7490, 4660},
58
        {10082, 6554, 4194},
59
        {9362, 5825, 3647},
60
        {8192, 5243, 3355},
61
        {7282, 4559, 2893},
62
}
63

64
// Quantize4x4 performs forward quantization on a 4x4 block.
65
// Returns quantized coefficients (levels).
66
func Quantize4x4(coeffs [16]int32, qp int) [16]int32 {
×
67
        var result [16]int32
×
68

×
69
        qpPer := qp / 6
×
70
        qpRem := qp % 6
×
71
        qBits := 15 + qpPer
×
72
        add := int32(1) << uint(qBits) / 3 // rounding for intra
×
73

×
NEW
74
        for i := range 16 {
×
75
                row := i / 4
×
76
                col := i % 4
×
77
                v := levelScaleIdx(row, col)
×
78
                mf := MF4x4[qpRem][v]
×
79

×
80
                sign := int32(1)
×
81
                c := coeffs[i]
×
82
                if c < 0 {
×
83
                        sign = -1
×
84
                        c = -c
×
85
                }
×
86
                result[i] = sign * ((c*mf + add) >> uint(qBits))
×
87
        }
88
        return result
×
89
}
90

91
// QuantizeDC4x4 performs forward quantization on 16x16 luma DC coefficients.
92
// wsDC is the scaling list DC value (typically 16 for flat default).
93
func QuantizeDC4x4(coeffs [16]int32, qp int, wsDC int32) [16]int32 {
1✔
94
        var result [16]int32
1✔
95

1✔
96
        qpPer := qp / 6
1✔
97
        qpRem := qp % 6
1✔
98
        mf := MF4x4[qpRem][0]
1✔
99
        // Adjust mf for scaling list: mf = mf * 16 / wsDC
1✔
100
        if wsDC != 16 {
1✔
101
                mf = mf * 16 / wsDC
×
102
        }
×
103
        qBits := 15 + qpPer + 1 // extra +1 for DC after Hadamard normalization
1✔
104
        add := int32(1) << uint(qBits) / 3
1✔
105

1✔
106
        for i := range 16 {
2✔
107
                sign := int32(1)
1✔
108
                c := coeffs[i]
1✔
109
                if c < 0 {
2✔
110
                        sign = -1
1✔
111
                        c = -c
1✔
112
                }
1✔
113
                result[i] = sign * ((c*mf + add) >> uint(qBits))
1✔
114
        }
115
        return result
1✔
116
}
117

118
// QuantizeChromaDC2x2 performs forward quantization on chroma DC coefficients (4:2:0).
119
func QuantizeChromaDC2x2(coeffs [4]int32, qpc int) [4]int32 {
1✔
120
        var result [4]int32
1✔
121

1✔
122
        qpPer := qpc / 6
1✔
123
        qpRem := qpc % 6
1✔
124
        mf := MF4x4[qpRem][0]
1✔
125
        qBits := 15 + qpPer + 1
1✔
126
        add := int32(1) << uint(qBits) / 3
1✔
127

1✔
128
        for i := range 4 {
2✔
129
                sign := int32(1)
1✔
130
                c := coeffs[i]
1✔
131
                if c < 0 {
2✔
132
                        sign = -1
1✔
133
                        c = -c
1✔
134
                }
1✔
135
                result[i] = sign * ((c*mf + add) >> uint(qBits))
1✔
136
        }
137
        return result
1✔
138
}
139

140
// levelScaleIdx maps (row, col) to LevelScale/MF index.
141
func levelScaleIdx(row, col int) int {
×
142
        r := row % 2
×
143
        c := col % 2
×
144
        if r == 0 && c == 0 {
×
145
                return 0
×
146
        }
×
147
        if r == 1 && c == 1 {
×
148
                return 2
×
149
        }
×
150
        return 1
×
151
}
152

153
// ChromaQP maps luma QP to chroma QP (Table 8-15).
154
func ChromaQP(qpY int) int {
1✔
155
        if qpY < 0 {
1✔
156
                qpY = 0
×
157
        }
×
158
        if qpY < 30 {
2✔
159
                return qpY
1✔
160
        }
1✔
161
        if qpY > 51 {
×
162
                return 51
×
163
        }
×
164
        qpcTable := []int{
×
165
                29, 30, 31, 32, 32, 33, 34, 34,
×
166
                35, 35, 36, 36, 37, 37, 37, 38,
×
167
                38, 38, 39, 39, 39, 39,
×
168
        }
×
169
        return qpcTable[qpY-30]
×
170
}
171

172
// ForwardTransformDC4x4 computes the DC coefficient of a 4x4 block
173
// from a constant residual value. For a constant block with value R:
174
// DC = 16*R (forward 4x4 DCT: row sum 4R, column sum 4*4R = 16R).
175
func ForwardTransformDC4x4(residual int32) int32 {
1✔
176
        return 16 * residual
1✔
177
}
1✔
178

179
// ForwardDequantRoundTrip verifies that forward quant + dequant of a DC value
180
// round-trips correctly. Used only for testing.
181
func ForwardDequantRoundTrip(dc int32, qp int) (quantized, dequantized int32) {
1✔
182
        // Forward Hadamard of 16 identical DC values
1✔
183
        var dcMatrix [16]int32
1✔
184
        for i := range dcMatrix {
2✔
185
                dcMatrix[i] = dc
1✔
186
        }
1✔
187
        hadamard := ForwardHadamard4x4(dcMatrix)
1✔
188

1✔
189
        // Forward quantize
1✔
190
        quantizedMatrix := QuantizeDC4x4(hadamard, qp, 16)
1✔
191

1✔
192
        // Inverse: dequant then inverse Hadamard
1✔
193
        dequantMatrix := transform.DequantDC4x4(quantizedMatrix, qp, 16)
1✔
194
        invHadamard := transform.InverseHadamard4x4(dequantMatrix)
1✔
195

1✔
196
        return quantizedMatrix[0], invHadamard[0]
1✔
197
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc