@@ -17,6 +17,11 @@ internal static class BitmapTranscoder
17
17
{
18
18
private static readonly int scatteringBase = Environment . ProcessorCount ;
19
19
20
+ struct ConversionConstants
21
+ {
22
+ public int multY , multUB , multUG , multVG , multVR , offsetY ;
23
+ }
24
+
20
25
// Prefered article: https://docs.microsoft.com/en-us/windows/win32/medfound/recommended-8-bit-yuv-formats-for-video-rendering#420-formats-16-bits-per-pixel
21
26
22
27
// some interesting code references:
@@ -25,11 +30,11 @@ internal static class BitmapTranscoder
25
30
// TranscodeFormats WITH FullRange suffix means that we suppose Y U and V are in [0..255] range
26
31
private static unsafe void TranscodeFromYUVInternal (
27
32
int width , int height ,
28
- TranscodeFormats conversionStandard , bool isUYVY ,
33
+ TranscodeFormats conversionStandard , NativeMethods . Compression compression ,
29
34
byte * pFrom , byte * pTo )
30
35
{
31
36
// constants for color conversion
32
- int multY , multUB , multUG , multVG , multVR , offsetY ;
37
+ ConversionConstants conversionConstants ;
33
38
34
39
// select constants for the color conversion
35
40
switch ( conversionStandard )
@@ -52,21 +57,28 @@ private static unsafe void TranscodeFromYUVInternal(
52
57
53
58
// multiply Y by 1.16438
54
59
// multiply UV by 1.13839
55
- multY = 298 ;
56
- multUB = 587 ;
57
- multUG = 114 ;
58
- multVG = 237 ;
59
- multVR = 466 ;
60
- offsetY = 16 ;
60
+ conversionConstants = new ConversionConstants
61
+ {
62
+ multY = 298 ,
63
+ multUB = 587 ,
64
+ multUG = 114 ,
65
+ multVG = 237 ,
66
+ multVR = 466 ,
67
+ offsetY = 16
68
+ } ;
61
69
break ;
62
70
63
71
case TranscodeFormats . BT601FullRange :
64
- multY = 255 ;
65
- multUB = 516 ;
66
- multUG = 100 ;
67
- multVG = 208 ;
68
- multVR = 409 ;
69
- offsetY = 0 ;
72
+
73
+ conversionConstants = new ConversionConstants
74
+ {
75
+ multY = 255 ,
76
+ multUB = 516 ,
77
+ multUG = 100 ,
78
+ multVG = 208 ,
79
+ multVR = 409 ,
80
+ offsetY = 0
81
+ } ;
70
82
break ;
71
83
72
84
//////////////////////////////////////////////////
@@ -87,21 +99,28 @@ private static unsafe void TranscodeFromYUVInternal(
87
99
88
100
// multiply Y by 1.16438
89
101
// multiply UV by 1.13839
90
- multY = 298 ;
91
- multUB = 541 ;
92
- multUG = 55 ;
93
- multVG = 137 ;
94
- multVR = 459 ;
95
- offsetY = 16 ;
102
+ conversionConstants = new ConversionConstants
103
+ {
104
+ multY = 298 ,
105
+ multUB = 541 ,
106
+ multUG = 55 ,
107
+ multVG = 137 ,
108
+ multVR = 459 ,
109
+ offsetY = 16
110
+ } ;
96
111
break ;
97
112
98
113
case TranscodeFormats . BT709FullRange :
99
- multY = 255 ;
100
- multUB = 475 ;
101
- multUG = 48 ;
102
- multVG = 120 ;
103
- multVR = 403 ;
104
- offsetY = 0 ;
114
+
115
+ conversionConstants = new ConversionConstants
116
+ {
117
+ multY = 255 ,
118
+ multUB = 475 ,
119
+ multUG = 48 ,
120
+ multVG = 120 ,
121
+ multVR = 403 ,
122
+ offsetY = 0
123
+ } ;
105
124
break ;
106
125
107
126
//////////////////////////////////////////////////
@@ -113,21 +132,29 @@ private static unsafe void TranscodeFromYUVInternal(
113
132
114
133
// multiply Y by 1.16438
115
134
// multiply UV by 1.13839
116
- multY = 298 ;
117
- multUB = 549 ;
118
- multUG = 48 ;
119
- multVG = 166 ;
120
- multVR = 429 ;
121
- offsetY = 16 ;
135
+
136
+ conversionConstants = new ConversionConstants
137
+ {
138
+ multY = 298 ,
139
+ multUB = 549 ,
140
+ multUG = 48 ,
141
+ multVG = 166 ,
142
+ multVR = 429 ,
143
+ offsetY = 16
144
+ } ;
122
145
break ;
123
146
124
147
case TranscodeFormats . BT2020FullRange :
125
- multY = 255 ;
126
- multUB = 482 ;
127
- multUG = 42 ;
128
- multVG = 146 ;
129
- multVR = 377 ;
130
- offsetY = 0 ;
148
+
149
+ conversionConstants = new ConversionConstants
150
+ {
151
+ multY = 255 ,
152
+ multUB = 482 ,
153
+ multUG = 42 ,
154
+ multVG = 146 ,
155
+ multVR = 377 ,
156
+ offsetY = 0
157
+ } ;
131
158
break ;
132
159
133
160
//////////////////////////////////////////////////
@@ -136,6 +163,88 @@ private static unsafe void TranscodeFromYUVInternal(
136
163
throw new ArgumentException ( nameof ( conversionStandard ) ) ;
137
164
}
138
165
166
+ switch ( compression )
167
+ {
168
+ case NativeMethods . Compression . UYVY :
169
+ case NativeMethods . Compression . HDYC :
170
+ YUY2_UYVY_to_RGB24 ( true , width , height , conversionConstants , compression , pFrom , pTo ) ;
171
+ break ;
172
+ case NativeMethods . Compression . YUYV :
173
+ case NativeMethods . Compression . YUY2 :
174
+ YUY2_UYVY_to_RGB24 ( false , width , height , conversionConstants , compression , pFrom , pTo ) ;
175
+ break ;
176
+ case NativeMethods . Compression . NV12 :
177
+ NV12_to_RGB24 ( width , height , conversionConstants , pFrom , pTo ) ;
178
+ break ;
179
+ default :
180
+ throw new ArgumentException ( nameof ( compression ) ) ;
181
+ }
182
+ }
183
+
184
+ private static unsafe void NV12_to_RGB24 (
185
+ int width , int height ,
186
+ ConversionConstants cconsts ,
187
+ byte * pFrom , byte * pTo )
188
+ {
189
+ var scatter = height / scatteringBase ;
190
+ Parallel . For ( 0 , ( height + scatter - 1 ) / scatter , ys =>
191
+ {
192
+ var y = ys * scatter ;
193
+ var myi = Math . Min ( height - y , scatter ) ;
194
+
195
+ for ( var yi = 0 ; yi < myi ; yi ++ )
196
+ {
197
+ byte * pFromY = pFrom + ( y + yi ) * width ;
198
+ byte * pFromUV = pFrom + ( height + ( y + yi ) / 2 ) * width ;
199
+
200
+ byte * pToBase = pTo + ( height - ( y + yi ) - 1 ) * width * 3 ;
201
+
202
+
203
+ for ( var x = 0 ; x < width - 1 ; x += 2 )
204
+ {
205
+ int c1 = pFromY [ 0 ] - cconsts . offsetY ; // Y1
206
+ int c2 = pFromY [ 1 ] - cconsts . offsetY ; // Y2
207
+ int d = pFromUV [ 0 ] - 128 ; // U
208
+ int e = pFromUV [ 1 ] - 128 ; // V
209
+
210
+ int cc1 = cconsts . multY * c1 ;
211
+ int cc2 = cconsts . multY * c2 ;
212
+
213
+ * pToBase ++ = Clip( ( cc1 + cconsts . multUB * d + 128 ) >> 8 ) ; // B1
214
+ * pToBase ++ = Clip( ( cc1 - cconsts . multUG * d - cconsts . multVG * e + 128 ) >> 8 ) ; // G1
215
+ * pToBase ++ = Clip( ( cc1 + cconsts . multVR * e + 128 ) >> 8 ) ; // R1
216
+
217
+ * pToBase ++ = Clip( ( cc2 + cconsts . multUB * d + 128 ) >> 8 ) ; // B2
218
+ * pToBase ++ = Clip( ( cc2 - cconsts . multUG * d - cconsts . multVG * e + 128 ) >> 8 ) ; // G2
219
+ * pToBase ++ = Clip( ( cc2 + cconsts . multVR * e + 128 ) >> 8 ) ; // R2
220
+
221
+ pFromY += 2 ;
222
+ pFromUV += 2 ;
223
+ }
224
+
225
+ if ( ( width & 1 ) != 0 )
226
+ {
227
+ int c1 = pFromY [ 0 ] - cconsts . offsetY ; // Y1
228
+ int d = pFromUV [ 0 ] - 128 ; // U
229
+ int e = pFromUV [ 1 ] - 128 ; // V
230
+
231
+ int cc1 = cconsts . multY * c1 ;
232
+
233
+ * pToBase ++ = Clip( ( cc1 + cconsts . multUB * d + 128 ) >> 8 ) ; // B1
234
+ * pToBase ++ = Clip( ( cc1 - cconsts . multUG * d - cconsts . multVG * e + 128 ) >> 8 ) ; // G1
235
+ * pToBase ++ = Clip( ( cc1 + cconsts . multVR * e + 128 ) >> 8 ) ; // R1
236
+ }
237
+ }
238
+ } ) ;
239
+ }
240
+
241
+ private static unsafe void YUY2_UYVY_to_RGB24 (
242
+ bool isUYVY ,
243
+ int width , int height ,
244
+ ConversionConstants cconsts ,
245
+ NativeMethods . Compression compression ,
246
+ byte * pFrom , byte * pTo )
247
+ {
139
248
var scatter = height / scatteringBase ;
140
249
Parallel . For ( 0 , ( height + scatter - 1 ) / scatter , ys =>
141
250
{
@@ -150,31 +259,33 @@ private static unsafe void TranscodeFromYUVInternal(
150
259
151
260
for ( var x = 0 ; x < width ; x += 2 )
152
261
{
262
+ // UYVY, HDYC
153
263
if ( isUYVY )
154
264
{
155
265
d = pFromBase [ 0 ] - 128 ; // U
156
- c1 = pFromBase [ 1 ] - offsetY ; // Y1
266
+ c1 = pFromBase [ 1 ] - cconsts . offsetY ; // Y1
157
267
e = pFromBase [ 2 ] - 128 ; // V
158
- c2 = pFromBase [ 3 ] - offsetY ; // Y2
268
+ c2 = pFromBase [ 3 ] - cconsts . offsetY ; // Y2
159
269
}
270
+ // YUY2, YUYV
160
271
else
161
272
{
162
- c1 = pFromBase [ 0 ] - offsetY ; // Y1
273
+ c1 = pFromBase [ 0 ] - cconsts . offsetY ; // Y1
163
274
d = pFromBase [ 1 ] - 128 ; // U
164
- c2 = pFromBase [ 2 ] - offsetY ; // Y2
275
+ c2 = pFromBase [ 2 ] - cconsts . offsetY ; // Y2
165
276
e = pFromBase [ 3 ] - 128 ; // V
166
277
}
167
278
168
- cc1 = multY * c1 ;
169
- cc2 = multY * c2 ;
279
+ cc1 = cconsts . multY * c1 ;
280
+ cc2 = cconsts . multY * c2 ;
170
281
171
- * pToBase ++ = Clip( ( cc1 + multUB * d + 128 ) >> 8 ) ; // B1
172
- * pToBase ++ = Clip( ( cc1 - multUG * d - multVG * e + 128 ) >> 8 ) ; // G1
173
- * pToBase ++ = Clip( ( cc1 + multVR * e + 128 ) >> 8 ) ; // R1
282
+ * pToBase ++ = Clip( ( cc1 + cconsts . multUB * d + 128 ) >> 8 ) ; // B1
283
+ * pToBase ++ = Clip( ( cc1 - cconsts . multUG * d - cconsts . multVG * e + 128 ) >> 8 ) ; // G1
284
+ * pToBase ++ = Clip( ( cc1 + cconsts . multVR * e + 128 ) >> 8 ) ; // R1
174
285
175
- * pToBase ++ = Clip( ( cc2 + multUB * d + 128 ) >> 8 ) ; // B2
176
- * pToBase ++ = Clip( ( cc2 - multUG * d - multVG * e + 128 ) >> 8 ) ; // G2
177
- * pToBase ++ = Clip( ( cc2 + multVR * e + 128 ) >> 8 ) ; // R2
286
+ * pToBase ++ = Clip( ( cc2 + cconsts . multUB * d + 128 ) >> 8 ) ; // B2
287
+ * pToBase ++ = Clip( ( cc2 - cconsts . multUG * d - cconsts . multVG * e + 128 ) >> 8 ) ; // G2
288
+ * pToBase ++ = Clip( ( cc2 + cconsts . multVR * e + 128 ) >> 8 ) ; // R2
178
289
179
290
pFromBase += 4 ;
180
291
}
@@ -184,7 +295,8 @@ private static unsafe void TranscodeFromYUVInternal(
184
295
185
296
private static unsafe void TranscodeFromYUV (
186
297
int width , int height ,
187
- TranscodeFormats transcodeFormat , bool isUYVY ,
298
+ TranscodeFormats transcodeFormat ,
299
+ NativeMethods . Compression compression ,
188
300
byte * pFrom , byte * pTo )
189
301
{
190
302
switch ( transcodeFormat )
@@ -195,16 +307,16 @@ private static unsafe void TranscodeFromYUV(
195
307
case TranscodeFormats . BT709FullRange :
196
308
case TranscodeFormats . BT2020 :
197
309
case TranscodeFormats . BT2020FullRange :
198
- TranscodeFromYUVInternal ( width , height , transcodeFormat , isUYVY , pFrom , pTo ) ;
310
+ TranscodeFromYUVInternal ( width , height , transcodeFormat , compression , pFrom , pTo ) ;
199
311
break ;
200
312
case TranscodeFormats . Auto :
201
313
// determine the color conversion based on the width and height of the frame
202
314
if ( width > 1920 || height > 1080 ) // UHD or larger
203
- TranscodeFromYUVInternal ( width , height , TranscodeFormats . BT2020 , isUYVY , pFrom , pTo ) ;
315
+ TranscodeFromYUVInternal ( width , height , TranscodeFormats . BT2020 , compression , pFrom , pTo ) ;
204
316
else if ( width > 720 || height > 576 ) // HD
205
- TranscodeFromYUVInternal ( width , height , TranscodeFormats . BT709 , isUYVY , pFrom , pTo ) ;
317
+ TranscodeFromYUVInternal ( width , height , TranscodeFormats . BT709 , compression , pFrom , pTo ) ;
206
318
else // SD
207
- TranscodeFromYUVInternal ( width , height , TranscodeFormats . BT601 , isUYVY , pFrom , pTo ) ;
319
+ TranscodeFromYUVInternal ( width , height , TranscodeFormats . BT601 , compression , pFrom , pTo ) ;
208
320
break ;
209
321
default :
210
322
throw new ArgumentException ( nameof ( transcodeFormat ) ) ;
@@ -228,6 +340,7 @@ private static byte Clip(int value) =>
228
340
case NativeMethods . Compression . YUYV :
229
341
case NativeMethods . Compression . YUY2 :
230
342
case NativeMethods . Compression . HDYC :
343
+ case NativeMethods . Compression . NV12 :
231
344
return width * height * 3 ;
232
345
default :
233
346
return null ;
@@ -244,11 +357,10 @@ public static unsafe void Transcode(
244
357
{
245
358
case NativeMethods . Compression . UYVY :
246
359
case NativeMethods . Compression . HDYC :
247
- TranscodeFromYUV ( width , height , transcodeFormat , true , pFrom , pTo ) ;
248
- break ;
249
360
case NativeMethods . Compression . YUYV :
250
361
case NativeMethods . Compression . YUY2 :
251
- TranscodeFromYUV ( width , height , transcodeFormat , false , pFrom , pTo ) ;
362
+ case NativeMethods . Compression . NV12 :
363
+ TranscodeFromYUV ( width , height , transcodeFormat , compression , pFrom , pTo ) ;
252
364
break ;
253
365
default :
254
366
throw new ArgumentException ( nameof ( compression ) ) ;
0 commit comments