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

Eyevinn / hi264 / 22354891696

24 Feb 2026 02:18PM UTC coverage: 86.968% (-0.6%) from 87.558%
22354891696

Pull #12

github

tobbee
feat: 8x8 block granularity, double-res text glyphs, and PlaneGrid

Add 8x8 block resolution for the encoder. Each grid character maps to
an 8x8 block (4 per macroblock) with proper AC residual encoding at
quadrant boundaries using a forward 4x4 integer DCT.

Introduce PlaneGrid type for direct Y/Cb/Cr value planes with no
character-count limit, supporting both 16x16 and 8x8 block granularity.
All CLI output paths now use PlaneGrid as intermediate representation.

Add 6x10 double-resolution font (Glyph2x) for 8x8 block mode, giving
4x the glyph detail of the 3x5 font at the same pixel footprint.
Even text scales (2, 4, 6...) in 16x16 mode auto-select the 2x font
for sharper glyphs without requiring -8x8.

Text character set aligned across both fonts: A-Z 0-9 and
! # % + - . / : = ? [ ] _ ( ) plus space. Lowercase a-z and
rarely-used punctuation removed; lowercase input auto-uppercased.

Bug fixes:
- CAVLC level VLC encoding overflow for large coefficients (QP=0)
- Encoder reconstruction order (inverse Hadamard before dequant)
- SMPTE bars distributed at block-column granularity in -8x8 mode
- CLI -8x8 flag takes priority over .gridimg default block size
Pull Request #12: feat: 8x8 block granularity, double-res text glyphs, and PlaneGrid

999 of 1182 new or added lines in 11 files covered. (84.52%)

6 existing lines in 4 files now uncovered.

7154 of 8226 relevant lines covered (86.97%)

1.0 hits per line

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

84.25
/pkg/encode/plane_encode.go
1
package encode
2

3
import (
4
        "github.com/Eyevinn/hi264/internal/cabac"
5
)
6

7
// scan2raster maps block scan index (0-15) to raster order (row*4+col) in the 4x4 grid.
8
// Computed from inverseRasterX4x4/Y4x4: raster = (by/4)*4 + (bx/4).
9
var scan2raster = [16]int{
10
        0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15,
11
}
12

13
// zigzag4x4 maps zig-zag scan position to raster position (Table 8-13).
14
var zigzag4x4 = [16]int{
15
        0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15,
16
}
17

18
// rasterToZigzag4x4 reorders a 16-element array from raster order to zig-zag scan order.
19
func rasterToZigzag4x4(raster [16]int32) [16]int32 {
1✔
20
        var zz [16]int32
1✔
21
        for scanPos, rasterPos := range zigzag4x4 {
2✔
22
                zz[scanPos] = raster[rasterPos]
1✔
23
        }
1✔
24
        return zz
1✔
25
}
26

27
// invZigzag4x4AC maps raster position (1-15) to zig-zag AC scan position (0-14).
28
// For AC coefficients, DC (position 0) is excluded, so zig-zag positions are 0-14
29
// mapping to raster positions {1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15}.
30
var zigzag4x4AC = [15]int{
31
        1, 4, 8, 5, 2, 3, 6,
32
        9, 12, 13, 10, 7, 11, 14, 15,
33
}
34

35
// rasterACToZigzag converts 15 AC coefficients from raster order (indices 1-15)
36
// to zig-zag scan order for the bitstream.
NEW
37
func rasterACToZigzag(rasterAC [15]int32) [15]int32 {
×
NEW
38
        var zz [15]int32
×
NEW
39
        for zzPos, rasterPos := range zigzag4x4AC {
×
NEW
40
                zz[zzPos] = rasterAC[rasterPos-1] // rasterAC is 0-indexed (positions 1-15 → indices 0-14)
×
NEW
41
        }
×
NEW
42
        return zz
×
43
}
44

45
// selectLumaModePlane picks the best I_16x16 luma prediction mode by evaluating
46
// per-pixel prediction error for all available modes. Returns the mode and the
47
// full per-pixel prediction array matching the decoder's behavior.
48
func selectLumaModePlane(reconY []uint8, strideY, mbX, mbY int, lumaVals [4]uint8) (mode int, predArray [256]uint8) {
1✔
49
        bestMode := 2
1✔
50
        bestPred := lumaPredict16x16(reconY, strideY, mbX, mbY, 2)
1✔
51
        bestErr := lumaPredErrorPlane(lumaVals, bestPred)
1✔
52

1✔
53
        if mbY > 0 {
2✔
54
                vPred := lumaPredict16x16(reconY, strideY, mbX, mbY, 0)
1✔
55
                vErr := lumaPredErrorPlane(lumaVals, vPred)
1✔
56
                if vErr < bestErr {
2✔
57
                        bestMode, bestPred, bestErr = 0, vPred, vErr
1✔
58
                }
1✔
59
        }
60

61
        if mbX > 0 {
2✔
62
                hPred := lumaPredict16x16(reconY, strideY, mbX, mbY, 1)
1✔
63
                hErr := lumaPredErrorPlane(lumaVals, hPred)
1✔
64
                if hErr < bestErr {
2✔
65
                        bestMode, bestPred, _ = 1, hPred, hErr
1✔
66
                }
1✔
67
        }
68

69
        return bestMode, bestPred
1✔
70
}
71

72
// lumaPredErrorPlane computes total absolute error between lumaVals (4 quadrants) and prediction array.
73
func lumaPredErrorPlane(lumaVals [4]uint8, pred [256]uint8) int {
1✔
74
        total := 0
1✔
75
        for y := range 16 {
2✔
76
                for x := range 16 {
2✔
77
                        qr, qc := y/8, x/8
1✔
78
                        val := int(lumaVals[qr*2+qc])
1✔
79
                        total += absInt(val - int(pred[y*16+x]))
1✔
80
                }
1✔
81
        }
82
        return total
1✔
83
}
84

85
// selectChromaModePlane picks the best chroma prediction mode using per-pixel predictions.
86
func selectChromaModePlane(reconCb, reconCr []uint8, strideC, mbX, mbY int,
87
        cbVals, crVals [4]uint8) (mode int, cbPred, crPred [64]uint8) {
1✔
88

1✔
89
        bestMode := 0
1✔
90
        bestCbPred := chromaPredict8x8(reconCb, strideC, mbX, mbY, 0)
1✔
91
        bestCrPred := chromaPredict8x8(reconCr, strideC, mbX, mbY, 0)
1✔
92
        bestErr := chromaPredErrorPlane(cbVals, crVals, bestCbPred, bestCrPred)
1✔
93

1✔
94
        if mbY > 0 {
2✔
95
                vCb := chromaPredict8x8(reconCb, strideC, mbX, mbY, 2)
1✔
96
                vCr := chromaPredict8x8(reconCr, strideC, mbX, mbY, 2)
1✔
97
                vErr := chromaPredErrorPlane(cbVals, crVals, vCb, vCr)
1✔
98
                if vErr < bestErr {
2✔
99
                        bestMode, bestCbPred, bestCrPred, bestErr = 2, vCb, vCr, vErr
1✔
100
                }
1✔
101
        }
102

103
        if mbX > 0 {
2✔
104
                hCb := chromaPredict8x8(reconCb, strideC, mbX, mbY, 1)
1✔
105
                hCr := chromaPredict8x8(reconCr, strideC, mbX, mbY, 1)
1✔
106
                hErr := chromaPredErrorPlane(cbVals, crVals, hCb, hCr)
1✔
107
                if hErr < bestErr {
2✔
108
                        bestMode, bestCbPred, bestCrPred, _ = 1, hCb, hCr, hErr
1✔
109
                }
1✔
110
        }
111

112
        return bestMode, bestCbPred, bestCrPred
1✔
113
}
114

115
// chromaPredErrorPlane computes total prediction error for chroma using per-pixel predictions.
116
func chromaPredErrorPlane(cbVals, crVals [4]uint8, cbPred, crPred [64]uint8) int {
1✔
117
        total := 0
1✔
118
        for blk := range 4 {
2✔
119
                x0 := (blk % 2) * 4
1✔
120
                y0 := (blk / 2) * 4
1✔
121
                for y := range 4 {
2✔
122
                        for x := range 4 {
2✔
123
                                idx := (y0+y)*8 + x0 + x
1✔
124
                                total += absInt(int(cbVals[blk]) - int(cbPred[idx]))
1✔
125
                                total += absInt(int(crVals[blk]) - int(crPred[idx]))
1✔
126
                        }
1✔
127
                }
128
        }
129
        return total
1✔
130
}
131

132
// chromaSubBlockDC computes the chroma DC coefficient for one 4x4 sub-block
133
// using per-pixel predictions. Returns the forward DCT DC = sum of residuals.
134
func chromaSubBlockDC(target uint8, predArray [64]uint8, x0, y0 int) int32 {
1✔
135
        sum := int32(0)
1✔
136
        for y := range 4 {
2✔
137
                for x := range 4 {
2✔
138
                        sum += int32(target) - int32(predArray[(y0+y)*8+x0+x])
1✔
139
                }
1✔
140
        }
141
        return sum
1✔
142
}
143

144
// reconstructLumaPixel performs per-pixel luma reconstruction using a per-pixel prediction array.
145
// This matches the decoder's behavior for all I_16x16 prediction modes.
146
func reconstructLumaPixel(quantDC [16]int32, quantAC [16][15]int32,
147
        predArray [256]uint8, qp int, lumaCBP int) [256]uint8 {
1✔
148

1✔
149
        var result [256]uint8
1✔
150

1✔
151
        invHadamard := inverseHadamard4x4(quantDC)
1✔
152
        dequantMatrix := dequantDC4x4(invHadamard, qp, 16)
1✔
153

1✔
154
        for blk := range 16 {
2✔
155
                bx := inverseRasterX4x4[blk]
1✔
156
                by := inverseRasterY4x4[blk]
1✔
157

1✔
158
                var dqBlock [16]int32
1✔
159
                dqBlock[0] = dequantMatrix[scan2raster[blk]]
1✔
160

1✔
161
                if lumaCBP != 0 {
1✔
NEW
162
                        qpPer := qp / 6
×
NEW
163
                        qpRem := qp % 6
×
NEW
164
                        for i := 1; i < 16; i++ {
×
NEW
165
                                row := i / 4
×
NEW
166
                                col := i % 4
×
NEW
167
                                v := levelScaleIdx(row, col)
×
NEW
168
                                ls := levelScale4x4[qpRem][v] * 16
×
NEW
169
                                if qpPer >= 4 {
×
NEW
170
                                        dqBlock[i] = quantAC[blk][i-1] * ls << uint(qpPer-4)
×
NEW
171
                                } else {
×
NEW
172
                                        dqBlock[i] = (quantAC[blk][i-1]*ls + (1 << uint(3-qpPer))) >> uint(4-qpPer)
×
NEW
173
                                }
×
174
                        }
175
                }
176

177
                dqBlock[0] += 32
1✔
178
                var temp [16]int32
1✔
179
                for i := range 4 {
2✔
180
                        z0 := dqBlock[i*4+0] + dqBlock[i*4+2]
1✔
181
                        z1 := dqBlock[i*4+0] - dqBlock[i*4+2]
1✔
182
                        z2 := (dqBlock[i*4+1] >> 1) - dqBlock[i*4+3]
1✔
183
                        z3 := dqBlock[i*4+1] + (dqBlock[i*4+3] >> 1)
1✔
184
                        temp[i*4+0] = z0 + z3
1✔
185
                        temp[i*4+1] = z1 + z2
1✔
186
                        temp[i*4+2] = z1 - z2
1✔
187
                        temp[i*4+3] = z0 - z3
1✔
188
                }
1✔
189
                for j := range 4 {
2✔
190
                        z0 := temp[0*4+j] + temp[2*4+j]
1✔
191
                        z1 := temp[0*4+j] - temp[2*4+j]
1✔
192
                        z2 := (temp[1*4+j] >> 1) - temp[3*4+j]
1✔
193
                        z3 := temp[1*4+j] + (temp[3*4+j] >> 1)
1✔
194

1✔
195
                        px := bx + j
1✔
196
                        for iy, residual := range [4]int32{
1✔
197
                                (z0 + z3) >> 6,
1✔
198
                                (z1 + z2) >> 6,
1✔
199
                                (z1 - z2) >> 6,
1✔
200
                                (z0 - z3) >> 6,
1✔
201
                        } {
2✔
202
                                py := by + iy
1✔
203
                                val := int32(predArray[py*16+px]) + residual
1✔
204
                                result[py*16+px] = clipU8(int(val))
1✔
205
                        }
1✔
206
                }
207
        }
208

209
        return result
1✔
210
}
211

212
// reconstructChromaPixel performs per-pixel chroma reconstruction for one plane.
213
// Each 4x4 sub-block gets a uniform DC residual added to the per-pixel prediction.
214
func reconstructChromaPixel(quantDC [4]int32, predArray [64]uint8, qpc int,
215
        recon []uint8, strideC, mbX, mbY int) {
1✔
216

1✔
217
        invH := inverseHadamard2x2(quantDC)
1✔
218
        dcScaled := dequantChromaDC2x2(invH, qpc, 16)
1✔
219

1✔
220
        for blk := range 4 {
2✔
221
                x0 := (blk % 2) * 4
1✔
222
                y0 := (blk / 2) * 4
1✔
223
                residual := (dcScaled[blk] + 32) >> 6
1✔
224
                for y := range 4 {
2✔
225
                        off := (mbY*8+y0+y)*strideC + mbX*8 + x0
1✔
226
                        for x := range 4 {
2✔
227
                                pred := int32(predArray[(y0+y)*8+x0+x])
1✔
228
                                recon[off+x] = clipU8(int(pred + residual))
1✔
229
                        }
1✔
230
                }
231
        }
232
}
233

234
// encodeMBPlane encodes a macroblock using PlaneGrid quad values (CAVLC).
235
func (e *FrameEncoder) encodeMBPlane(w *BitWriter, mbX, mbY int,
236
        lumaVals [4]uint8, cbVals, crVals [4]uint8,
237
        nCLuma [][]int, nCCb, nCCr [][]int,
238
        reconY []uint8, strideY int, reconCb, reconCr []uint8, strideC int) error {
1✔
239

1✔
240
        qp := e.QP
1✔
241
        qpc := ChromaQP(qp)
1✔
242

1✔
243
        // Select best luma prediction mode using per-pixel predictions
1✔
244
        lumaMode, lumaPredArray := selectLumaModePlane(reconY, strideY, mbX, mbY, lumaVals)
1✔
245

1✔
246
        // Compute per-4x4-block DC and AC coefficients using per-pixel residuals
1✔
247
        var dcMatrix [16]int32
1✔
248
        var acCoeffs [16][15]int32
1✔
249
        lumaCBP := 0
1✔
250

1✔
251
        for blk := range 16 {
2✔
252
                bx := inverseRasterX4x4[blk]
1✔
253
                by := inverseRasterY4x4[blk]
1✔
254
                var res [16]int32
1✔
255
                for r := range 4 {
2✔
256
                        for c := range 4 {
2✔
257
                                py, px := by+r, bx+c
1✔
258
                                qr, qc := py/8, px/8
1✔
259
                                val := lumaVals[qr*2+qc]
1✔
260
                                res[r*4+c] = int32(val) - int32(lumaPredArray[py*16+px])
1✔
261
                        }
1✔
262
                }
263
                fwd := ForwardTransform4x4(res)
1✔
264
                dcMatrix[scan2raster[blk]] = fwd[0]
1✔
265
                copy(acCoeffs[blk][:], fwd[1:])
1✔
266
        }
267

268
        hadamardResult := ForwardHadamard4x4(dcMatrix)
1✔
269
        quantDC := QuantizeDC4x4(hadamardResult, qp, 16)
1✔
270

1✔
271
        // Quantize AC coefficients and check if any are non-zero
1✔
272
        var quantAC [16][15]int32
1✔
273
        for blk := range 16 {
2✔
274
                var fullBlock [16]int32
1✔
275
                copy(fullBlock[1:], acCoeffs[blk][:])
1✔
276
                qBlock := Quantize4x4(fullBlock, qp)
1✔
277
                copy(quantAC[blk][:], qBlock[1:])
1✔
278
                for _, v := range quantAC[blk] {
2✔
279
                        if v != 0 {
1✔
NEW
280
                                lumaCBP = 1
×
NEW
281
                                break
×
282
                        }
283
                }
284
        }
285

286
        // Select best chroma prediction mode using per-pixel predictions
287
        chromaMode, cbPredArray, crPredArray := selectChromaModePlane(reconCb, reconCr, strideC, mbX, mbY, cbVals, crVals)
1✔
288

1✔
289
        // Chroma DC: per-sub-block residuals using exact sum
1✔
290
        var cbDCMatrix [4]int32
1✔
291
        var crDCMatrix [4]int32
1✔
292
        for i := range 4 {
2✔
293
                x0 := (i % 2) * 4
1✔
294
                y0 := (i / 2) * 4
1✔
295
                cbDCMatrix[i] = chromaSubBlockDC(cbVals[i], cbPredArray, x0, y0)
1✔
296
                crDCMatrix[i] = chromaSubBlockDC(crVals[i], crPredArray, x0, y0)
1✔
297
        }
1✔
298
        cbHadamard := ForwardHadamard2x2(cbDCMatrix)
1✔
299
        crHadamard := ForwardHadamard2x2(crDCMatrix)
1✔
300
        quantCbDC := QuantizeChromaDC2x2(cbHadamard, qpc)
1✔
301
        quantCrDC := QuantizeChromaDC2x2(crHadamard, qpc)
1✔
302

1✔
303
        chromaCBP := 0
1✔
304
        for i := range 4 {
2✔
305
                if quantCbDC[i] != 0 || quantCrDC[i] != 0 {
2✔
306
                        chromaCBP = 1
1✔
307
                        break
1✔
308
                }
309
        }
310

311
        // Determine I_16x16 mb_type
312
        mbType := 1 + lumaMode + 4*chromaCBP + 12*lumaCBP
1✔
313

1✔
314
        // Write mb_type as ue(v)
1✔
315
        w.WriteUE(uint32(mbType))
1✔
316

1✔
317
        // intra_chroma_pred_mode
1✔
318
        w.WriteUE(uint32(chromaMode))
1✔
319

1✔
320
        // mb_qp_delta = 0
1✔
321
        w.WriteSE(0)
1✔
322

1✔
323
        // Intra16x16DCLevel — bitstream uses zig-zag scan order
1✔
324
        dcZZ := rasterToZigzag4x4(quantDC)
1✔
325
        nCDC := computeNC4x4(nCLuma, mbX*4, mbY*4)
1✔
326
        EncodeResidualBlock(w, dcZZ[:], nCDC, 16)
1✔
327

1✔
328
        // Intra16x16ACLevel — bitstream uses zig-zag scan order (skipping DC)
1✔
329
        if lumaCBP != 0 {
1✔
NEW
330
                for blk := range 16 {
×
NEW
331
                        bx := inverseRasterX4x4[blk]
×
NEW
332
                        by := inverseRasterY4x4[blk]
×
NEW
333
                        acZZ := rasterACToZigzag(quantAC[blk])
×
NEW
334
                        nC := computeNC4x4(nCLuma, mbX*4+bx/4, mbY*4+by/4)
×
NEW
335
                        nNZ := EncodeResidualBlock(w, acZZ[:], nC, 15)
×
NEW
336
                        nCLuma[mbY*4+by/4][mbX*4+bx/4] = nNZ
×
NEW
337
                }
×
338
        } else {
1✔
339
                for blk := range 16 {
2✔
340
                        bx := inverseRasterX4x4[blk]
1✔
341
                        by := inverseRasterY4x4[blk]
1✔
342
                        nCLuma[mbY*4+by/4][mbX*4+bx/4] = 0
1✔
343
                }
1✔
344
        }
345

346
        // Chroma
347
        if chromaCBP > 0 {
2✔
348
                EncodeResidualBlock(w, quantCbDC[:], -1, 4)
1✔
349
                EncodeResidualBlock(w, quantCrDC[:], -1, 4)
1✔
350
        }
1✔
351

352
        // Update chroma nC (all zero for DC-only chroma blocks)
353
        for blk := range 4 {
2✔
354
                bx := (blk % 2)
1✔
355
                by := (blk / 2)
1✔
356
                nCCb[mbY*2+by][mbX*2+bx] = 0
1✔
357
                nCCr[mbY*2+by][mbX*2+bx] = 0
1✔
358
        }
1✔
359

360
        // Update reconstructed luma pixels (per-pixel prediction)
361
        reconLuma := reconstructLumaPixel(quantDC, quantAC, lumaPredArray, qp, lumaCBP)
1✔
362
        for y := range 16 {
2✔
363
                off := (mbY*16+y)*strideY + mbX*16
1✔
364
                copy(reconY[off:off+16], reconLuma[y*16:y*16+16])
1✔
365
        }
1✔
366

367
        // Update reconstructed chroma pixels (per-pixel prediction)
368
        reconstructChromaPixel(quantCbDC, cbPredArray, qpc, reconCb, strideC, mbX, mbY)
1✔
369
        reconstructChromaPixel(quantCrDC, crPredArray, qpc, reconCr, strideC, mbX, mbY)
1✔
370

1✔
371
        return nil
1✔
372
}
373

374
// encodeMBCABACPlane encodes a macroblock using PlaneGrid quad values (CABAC).
375
func (e *FrameEncoder) encodeMBCABACPlane(enc *cabac.Encoder, ctx []cabac.CtxState,
376
        mbStates []encMBState, mbIdx, mbX, mbY int,
377
        lumaVals [4]uint8, cbVals, crVals [4]uint8,
378
        mbWidth, _ int,
379
        reconY []uint8, strideY int, reconCb, reconCr []uint8, strideC int) error {
1✔
380

1✔
381
        qp := e.QP
1✔
382
        qpc := ChromaQP(qp)
1✔
383

1✔
384
        // Select best luma prediction mode using per-pixel predictions
1✔
385
        lumaMode, lumaPredArray := selectLumaModePlane(reconY, strideY, mbX, mbY, lumaVals)
1✔
386

1✔
387
        // Compute per-4x4-block DC and AC coefficients using per-pixel residuals
1✔
388
        var dcMatrix [16]int32
1✔
389
        var acCoeffs [16][15]int32
1✔
390
        lumaCBP := 0
1✔
391

1✔
392
        for blk := range 16 {
2✔
393
                bx := inverseRasterX4x4[blk]
1✔
394
                by := inverseRasterY4x4[blk]
1✔
395
                var res [16]int32
1✔
396
                for r := range 4 {
2✔
397
                        for c := range 4 {
2✔
398
                                py, px := by+r, bx+c
1✔
399
                                qr, qc := py/8, px/8
1✔
400
                                val := lumaVals[qr*2+qc]
1✔
401
                                res[r*4+c] = int32(val) - int32(lumaPredArray[py*16+px])
1✔
402
                        }
1✔
403
                }
404
                fwd := ForwardTransform4x4(res)
1✔
405
                dcMatrix[scan2raster[blk]] = fwd[0]
1✔
406
                copy(acCoeffs[blk][:], fwd[1:])
1✔
407
        }
408

409
        hadamardResult := ForwardHadamard4x4(dcMatrix)
1✔
410
        quantDC := QuantizeDC4x4(hadamardResult, qp, 16)
1✔
411

1✔
412
        // Quantize AC coefficients and check if any are non-zero
1✔
413
        var quantAC [16][15]int32
1✔
414
        for blk := range 16 {
2✔
415
                var fullBlock [16]int32
1✔
416
                copy(fullBlock[1:], acCoeffs[blk][:])
1✔
417
                qBlock := Quantize4x4(fullBlock, qp)
1✔
418
                copy(quantAC[blk][:], qBlock[1:])
1✔
419
                for _, v := range quantAC[blk] {
2✔
420
                        if v != 0 {
1✔
NEW
421
                                lumaCBP = 1
×
NEW
422
                                break
×
423
                        }
424
                }
425
        }
426

427
        // Select best chroma prediction mode using per-pixel predictions
428
        chromaMode, cbPredArray, crPredArray := selectChromaModePlane(reconCb, reconCr, strideC, mbX, mbY, cbVals, crVals)
1✔
429

1✔
430
        // Chroma DC: per-sub-block residuals using exact sum
1✔
431
        var cbDCMatrix [4]int32
1✔
432
        var crDCMatrix [4]int32
1✔
433
        for i := range 4 {
2✔
434
                x0 := (i % 2) * 4
1✔
435
                y0 := (i / 2) * 4
1✔
436
                cbDCMatrix[i] = chromaSubBlockDC(cbVals[i], cbPredArray, x0, y0)
1✔
437
                crDCMatrix[i] = chromaSubBlockDC(crVals[i], crPredArray, x0, y0)
1✔
438
        }
1✔
439
        cbHadamard := ForwardHadamard2x2(cbDCMatrix)
1✔
440
        crHadamard := ForwardHadamard2x2(crDCMatrix)
1✔
441
        quantCbDC := QuantizeChromaDC2x2(cbHadamard, qpc)
1✔
442
        quantCrDC := QuantizeChromaDC2x2(crHadamard, qpc)
1✔
443

1✔
444
        chromaCBP := 0
1✔
445
        for i := range 4 {
2✔
446
                if quantCbDC[i] != 0 || quantCrDC[i] != 0 {
2✔
447
                        chromaCBP = 1
1✔
448
                        break
1✔
449
                }
450
        }
451

452
        mbType := 1 + lumaMode + 4*chromaCBP + 12*lumaCBP
1✔
453

1✔
454
        // --- CABAC encoding of syntax elements ---
1✔
455

1✔
456
        var leftState, topState *encMBState
1✔
457
        if mbX > 0 {
2✔
458
                leftState = &mbStates[mbIdx-1]
1✔
459
        }
1✔
460
        if mbY > 0 {
2✔
461
                topState = &mbStates[mbIdx-mbWidth]
1✔
462
        }
1✔
463

464
        leftNotINxN := leftState != nil
1✔
465
        topNotINxN := topState != nil
1✔
466
        encodeMBTypeI16x16(enc, ctx, mbType, leftNotINxN, topNotINxN)
1✔
467

1✔
468
        leftChromaNZ := leftState != nil && leftState.intraChromaPredMode != 0
1✔
469
        topChromaNZ := topState != nil && topState.intraChromaPredMode != 0
1✔
470
        encodeChromaPredMode(enc, ctx, chromaMode, leftChromaNZ, topChromaNZ)
1✔
471

1✔
472
        encodeQPDelta(enc, ctx, 0, false)
1✔
473

1✔
474
        // Residual: Intra16x16DCLevel — bitstream uses zig-zag scan order
1✔
475
        dcCBFCtx := deriveDCCBFCtx(leftState, topState)
1✔
476
        dcZZ := rasterToZigzag4x4(quantDC)
1✔
477
        dcCBF := encodeResidualBlockCABAC(enc, ctx, encCtxBlockCatIntra16x16DC, dcCBFCtx, dcZZ[:], 16)
1✔
478
        mbStates[mbIdx].codedBlockFlag[encCtxBlockCatIntra16x16DC][0] = dcCBF
1✔
479

1✔
480
        // Intra16x16ACLevel — bitstream uses zig-zag scan order (skipping DC)
1✔
481
        if lumaCBP != 0 {
1✔
NEW
482
                for blk := range 16 {
×
NEW
483
                        cbfCtx := deriveACCBFCtx(mbStates, mbIdx, mbX, mbY, mbWidth, blk)
×
NEW
484
                        acZZ := rasterACToZigzag(quantAC[blk])
×
NEW
485
                        cbf := encodeResidualBlockCABAC(enc, ctx, encCtxBlockCatIntra16x16AC, cbfCtx, acZZ[:], 15)
×
NEW
486
                        mbStates[mbIdx].codedBlockFlag[encCtxBlockCatIntra16x16AC][blk] = cbf
×
NEW
487
                }
×
488
        } else {
1✔
489
                for blk := range 16 {
2✔
490
                        mbStates[mbIdx].codedBlockFlag[encCtxBlockCatIntra16x16AC][blk] = 0
1✔
491
                }
1✔
492
        }
493

494
        // Chroma DC
495
        if chromaCBP > 0 {
2✔
496
                cbCBFCtx := deriveChromaDCCBFCtx(leftState, topState, 0)
1✔
497
                cbCBF := encodeResidualBlockCABAC(enc, ctx, encCtxBlockCatChromaDC, cbCBFCtx, quantCbDC[:], 4)
1✔
498
                mbStates[mbIdx].codedBlockFlag[encCtxBlockCatChromaDC][0] = cbCBF
1✔
499

1✔
500
                crCBFCtx := deriveChromaDCCBFCtx(leftState, topState, 1)
1✔
501
                crCBF := encodeResidualBlockCABAC(enc, ctx, encCtxBlockCatChromaDC, crCBFCtx, quantCrDC[:], 4)
1✔
502
                mbStates[mbIdx].codedBlockFlag[encCtxBlockCatChromaDC][1] = crCBF
1✔
503
        }
1✔
504

505
        // Update MB state
506
        mbStates[mbIdx].mbType = mbType
1✔
507
        mbStates[mbIdx].intraChromaPredMode = chromaMode
1✔
508
        mbStates[mbIdx].cbpChroma = chromaCBP
1✔
509

1✔
510
        // Update reconstructed luma pixels (per-pixel prediction)
1✔
511
        reconLuma := reconstructLumaPixel(quantDC, quantAC, lumaPredArray, qp, lumaCBP)
1✔
512
        for y := range 16 {
2✔
513
                off := (mbY*16+y)*strideY + mbX*16
1✔
514
                copy(reconY[off:off+16], reconLuma[y*16:y*16+16])
1✔
515
        }
1✔
516

517
        // Update reconstructed chroma pixels (per-pixel prediction)
518
        reconstructChromaPixel(quantCbDC, cbPredArray, qpc, reconCb, strideC, mbX, mbY)
1✔
519
        reconstructChromaPixel(quantCrDC, crPredArray, qpc, reconCr, strideC, mbX, mbY)
1✔
520

1✔
521
        return nil
1✔
522
}
523

524
// deriveACCBFCtx derives the coded_block_flag context for Intra16x16AC blocks.
NEW
525
func deriveACCBFCtx(mbStates []encMBState, mbIdx, mbX, mbY, mbWidth, blk int) int {
×
NEW
526
        bx := inverseRasterX4x4[blk] / 4 // 0-3 column index within MB
×
NEW
527
        by := inverseRasterY4x4[blk] / 4 // 0-3 row index within MB
×
NEW
528

×
NEW
529
        condA := 1 // default when neighbor unavailable
×
NEW
530
        condB := 1
×
NEW
531

×
NEW
532
        // Left neighbor
×
NEW
533
        if bx > 0 {
×
NEW
534
                // Same MB: find the block to the left
×
NEW
535
                leftBlk := findBlock4x4(bx-1, by)
×
NEW
536
                condA = int(mbStates[mbIdx].codedBlockFlag[encCtxBlockCatIntra16x16AC][leftBlk])
×
NEW
537
        } else if mbX > 0 {
×
NEW
538
                // Previous MB: rightmost column (bx=3)
×
NEW
539
                leftBlk := findBlock4x4(3, by)
×
NEW
540
                condA = int(mbStates[mbIdx-1].codedBlockFlag[encCtxBlockCatIntra16x16AC][leftBlk])
×
NEW
541
        }
×
542

543
        // Top neighbor
NEW
544
        if by > 0 {
×
NEW
545
                topBlk := findBlock4x4(bx, by-1)
×
NEW
546
                condB = int(mbStates[mbIdx].codedBlockFlag[encCtxBlockCatIntra16x16AC][topBlk])
×
NEW
547
        } else if mbY > 0 {
×
NEW
548
                topBlk := findBlock4x4(bx, 3)
×
NEW
549
                condB = int(mbStates[mbIdx-mbWidth].codedBlockFlag[encCtxBlockCatIntra16x16AC][topBlk])
×
NEW
550
        }
×
551

NEW
552
        return condA + 2*condB
×
553
}
554

555
// findBlock4x4 returns the block index for the 4x4 block at position (bx, by) within an MB.
556
// bx and by are in 4x4-block units (0-3).
NEW
557
func findBlock4x4(bx, by int) int {
×
NEW
558
        // Inverse of inverseRasterX4x4/inverseRasterY4x4: find block index from (x,y) position.
×
NEW
559
        // The 4x4 block scan order maps (bx,by) → block index.
×
NEW
560
        return rasterScan4x4[by*4+bx]
×
NEW
561
}
×
562

563
// rasterScan4x4 maps (row*4+col) in 4x4-block units to block scan index.
564
var rasterScan4x4 = [16]int{
565
        0, 1, 4, 5,
566
        2, 3, 6, 7,
567
        8, 9, 12, 13,
568
        10, 11, 14, 15,
569
}
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