@@ -1325,85 +1325,86 @@ static void Draw_VectorScope_circle_targets_axes(int bits_per_pixel,
13251325 pixel_t * dstp_u = reinterpret_cast <pixel_t *>(dstp8_u);
13261326 pixel_t * dstp_v = reinterpret_cast <pixel_t *>(dstp8_v);
13271327
1328- if (params.targets ) {
1329- // ColorBars ground truth linear RGB (0.75 amplitude, Rec. BT.801-1)
1330- // Top 2/3 bars: LtGrey, Yellow, Cyan, Green, Magenta, Red, Blue
1331- // (White/LtGrey sits at UV center, not a useful vectorscope target)
1332- struct RGBEntry { const char * name; double r, g, b; };
1333- static const RGBEntry bar_rgb[] = {
1334- { " Yellow" , 0.75 , 0.75 , 0.0 },
1335- { " Cyan" , 0.0 , 0.75 , 0.75 },
1336- { " Green" , 0.0 , 0.75 , 0.0 },
1337- { " Magenta" , 0.75 , 0.0 , 0.75 },
1338- { " Red" , 0.75 , 0.0 , 0.0 },
1339- { " Blue" , 0.0 , 0.0 , 0.75 },
1340- };
1328+ // ===== -I AND +Q SIGNAL RGB VALUES FOR VECTORSCOPE GRATICULE =====
1329+ // These feed through the same matrix-aware make_yuv path as the color bar patches,
1330+ // giving correct UV pixel coordinates for any matrix and any bit depth.
1331+ //
1332+ // Note: -I and +Q are analog NTSC test signals that don't map cleanly to digital RGB/YUV.
1333+ // We maintain two separate specifications for backward compatibility:
1334+ //
1335+ // RGB-NATIVE: Lifted to studio black (code 16), broadcast-safe
1336+ // Used when vectorscope processes RGB sources or when drawing RGB graticules
1337+ // -I: RGB(16, 90, 130) at 8-bit → produces Y≈77 after matrix conversion
1338+ // +Q: RGB(92, 16, 143) at 8-bit → produces Y≈63 after matrix conversion
1339+ //
1340+ // YUV-TARGETED: Zero-luma pure chroma definition
1341+ // Used when vectorscope processes YUV sources
1342+ // -I: Converts to Y=16, Cb=158, Cr=95 (legacy ColorBars YUV output)
1343+ // +Q: Converts to Y=16, Cb=174, Cr=149 (legacy ColorBars YUV output)
1344+ // Contains out-of-range RGB components (super-blacks)
1345+ //
1346+ // Note 2: These will not lay exactly on UV's 33° and 123° degree lines, since those
1347+ // angles are defined in the original NTSC YIQ color space, which differs from the YUV
1348+ // space produced by BT.601/BT.709 matrix conversions.
1349+
1350+ // this one is not used, this is YUV vectorscope, we cannot determine that the YUV was converted from
1351+ // an RGB ColorBars.
1352+ /*
1353+ static const RGBEntry iq_rgb_native[] = {
1354+ { "-I", MINUS_I_R, MINUS_I_G, MINUS_I_B }, // RGB-native (studio black lift)
1355+ { "+Q", PLUS_Q_R, PLUS_Q_G, PLUS_Q_B }, // RGB-native (studio black lift)
1356+ { "+I", PLUS_I_R, PLUS_I_G, PLUS_I_B }, // n/a ColorBarsHD only, which is YUV-only
1357+ };
1358+ */
1359+
1360+ // ColorBars ground truth linear RGB (0.75 amplitude, Rec. BT.801-1)
1361+ // Top 2/3 bars: LtGrey, Yellow, Cyan, Green, Magenta, Red, Blue
1362+ // (White/LtGrey sits at UV center, not a useful vectorscope target)
1363+ struct RGBEntry { const char * name; double r, g, b; };
1364+ static const RGBEntry bar_rgb[] = {
1365+ { " Yellow" , 0.75 , 0.75 , 0.0 },
1366+ { " Cyan" , 0.0 , 0.75 , 0.75 },
1367+ { " Green" , 0.0 , 0.75 , 0.0 },
1368+ { " Magenta" , 0.75 , 0.0 , 0.75 },
1369+ { " Red" , 0.75 , 0.0 , 0.0 },
1370+ { " Blue" , 0.0 , 0.0 , 0.75 },
1371+ };
13411372
1342- // ===== -I AND +Q SIGNAL RGB VALUES FOR VECTORSCOPE GRATICULE =====
1343- // These feed through the same matrix-aware make_yuv path as the color bar patches,
1344- // giving correct UV pixel coordinates for any matrix and any bit depth.
1345- //
1346- // Note: -I and +Q are analog NTSC test signals that don't map cleanly to digital RGB/YUV.
1347- // We maintain two separate specifications for backward compatibility:
1348- //
1349- // RGB-NATIVE: Lifted to studio black (code 16), broadcast-safe
1350- // Used when vectorscope processes RGB sources or when drawing RGB graticules
1351- // -I: RGB(16, 90, 130) at 8-bit → produces Y≈77 after matrix conversion
1352- // +Q: RGB(92, 16, 143) at 8-bit → produces Y≈63 after matrix conversion
1353- //
1354- // YUV-TARGETED: Zero-luma pure chroma definition
1355- // Used when vectorscope processes YUV sources
1356- // -I: Converts to Y=16, Cb=158, Cr=95 (legacy ColorBars YUV output)
1357- // +Q: Converts to Y=16, Cb=174, Cr=149 (legacy ColorBars YUV output)
1358- // Contains out-of-range RGB components (super-blacks)
1359- //
1360- // Note 2: These will not lay exactly on UV's 33° and 123° degree lines, since those
1361- // angles are defined in the original NTSC YIQ color space, which differs from the YUV
1362- // space produced by BT.601/BT.709 matrix conversions.
1363-
1364- // this one is not used, this is YUV vectorscope, we cannot determine that the YUV was converted from
1365- // an RGB ColorBars.
1366- /*
1367- static const RGBEntry iq_rgb_native[] = {
1368- { "-I", MINUS_I_R, MINUS_I_G, MINUS_I_B }, // RGB-native (studio black lift)
1369- { "+Q", PLUS_Q_R, PLUS_Q_G, PLUS_Q_B }, // RGB-native (studio black lift)
1370- { "+I", PLUS_I_R, PLUS_I_G, PLUS_I_B }, // n/a ColorBarsHD only, which is YUV-only
1371- };
1372- */
1373+ static const RGBEntry iq_rgb_yuv_targeted[] = {
1374+ { " -I" , MINUS_I_R_YUV , MINUS_I_G_YUV , MINUS_I_B_YUV }, // YUV-targeted (zero-luma)
1375+ { " +Q" , PLUS_Q_R_YUV , PLUS_Q_G_YUV , PLUS_Q_B_YUV }, // YUV-targeted (zero-luma)
1376+ { " +I" , PLUS_I_R_YUV , PLUS_I_G_YUV , PLUS_I_B_YUV }, // ColorBarsHD only (same for both)
1377+ };
13731378
1374- static const RGBEntry iq_rgb_yuv_targeted[] = {
1375- { " -I" , MINUS_I_R_YUV , MINUS_I_G_YUV , MINUS_I_B_YUV }, // YUV-targeted (zero-luma)
1376- { " +Q" , PLUS_Q_R_YUV , PLUS_Q_G_YUV , PLUS_Q_B_YUV }, // YUV-targeted (zero-luma)
1377- { " +I" , PLUS_I_R_YUV , PLUS_I_G_YUV , PLUS_I_B_YUV }, // ColorBarsHD only (same for both)
1378- };
1379+ const int half_box = 4 * (1 << (show_bits - 8 ));
13791380
1380- // Box color (brownish, visible on the wheel background) ----
1381- pixel_t box_luma, box_u, box_v;
1382- if constexpr (std::is_integral<pixel_t >::value) {
1383- // Tan/beige: Y=80, slightly orange/brown tint
1384- box_luma = (pixel_t )(80 << (bits_per_pixel - 8 ));
1385- box_u = (pixel_t )(140 << (bits_per_pixel - 8 ));
1386- box_v = (pixel_t )(120 << (bits_per_pixel - 8 ));
1387- }
1388- else {
1389- box_luma = c8tof (80 );
1390- box_u = uv8tof (140 );
1391- box_v = uv8tof (120 );
1392- }
1381+ // ---- Pixel position helper ----
1382+ // Converts normalised chroma [-0.5..0.5] to vectorscope pixel coordinate.
1383+ // Always from full range float, since the YUV squares are obtained from the linear RGB values.
1384+ // Target is limited/full range aware.
1385+ auto uv_to_px_float = [&](double uv_norm) -> int {
1386+ return clamp (
1387+ (int )((float )uv_norm * conv_consts_fullrange_float_origin.mul_factor + conv_consts_fullrange_float_origin.dst_offset + 0 .5f ),
1388+ 0 , (1 << show_bits) - 1 );
1389+ };
13931390
1394- const int half_box = 4 * (1 << (show_bits - 8 ));
1391+ // Box color (brownish, visible on the wheel background) ----
1392+ pixel_t box_luma, box_u, box_v;
1393+ if constexpr (std::is_integral<pixel_t >::value) {
1394+ // Tan/beige: Y=80, slightly orange/brown tint
1395+ box_luma = (pixel_t )(80 << (bits_per_pixel - 8 ));
1396+ box_u = (pixel_t )(140 << (bits_per_pixel - 8 ));
1397+ box_v = (pixel_t )(120 << (bits_per_pixel - 8 ));
1398+ }
1399+ else {
1400+ box_luma = c8tof (80 );
1401+ box_u = uv8tof (140 );
1402+ box_v = uv8tof (120 );
1403+ }
13951404
1396- // ---- Pixel position helper ----
1397- // Converts normalised chroma [-0.5..0.5] to vectorscope pixel coordinate.
1398- // Always from full range float, since the YUV squares are obtained from the linear RGB values.
1399- // Target is limited/full range aware.
1400- auto uv_to_px_float = [&](double uv_norm) -> int {
1401- return clamp (
1402- (int )((float )uv_norm * conv_consts_fullrange_float_origin.mul_factor + conv_consts_fullrange_float_origin.dst_offset + 0 .5f ),
1403- 0 , (1 << show_bits) - 1 );
1404- };
1405+ if (params.targets ) {
14051406
1406- // ---- Draw colour-bar target boxes ----
1407+ // ---- Draw colorbar target boxes ----
14071408 for (auto & e : bar_rgb) {
14081409 double dY, dU, dV;
14091410 GetYUVFromMatrix (matrix, e.r , e.g , e.b , dY, dU, dV);
@@ -1426,62 +1427,62 @@ static void Draw_VectorScope_circle_targets_axes(int bits_per_pixel,
14261427 limit_showwidth);
14271428
14281429 }
1430+ }
14291431
1430- if (params.iq ) {
1431- // ---- Draw +Q and -I target boxes ----
1432- // These are matrix-independent (they are phase references, not primaries),
1433- // but still need the same pixel-coordinate conversion.
1434- for (auto & e : iq_rgb_yuv_targeted) {
1435- double dY, dU, dV;
1436- GetYUVFromMatrix (matrix, e.r , e.g , e.b , dY, dU, dV);
1437- int cx = uv_to_px_float (dU);
1438- int cy = uv_to_px_float (dV);
1439- // yuv.u and yuv.v are already the correct pixel_t values at the virtual target
1440- // (show) bit depth.
1432+ if (params.iq ) {
1433+ // ---- Draw +Q and -I target boxes ----
1434+ // These are matrix-independent (they are phase references, not primaries),
1435+ // but still need the same pixel-coordinate conversion.
1436+ for (auto & e : iq_rgb_yuv_targeted) {
1437+ double dY, dU, dV;
1438+ GetYUVFromMatrix (matrix, e.r , e.g , e.b , dY, dU, dV);
1439+ int cx = uv_to_px_float (dU);
1440+ int cy = uv_to_px_float (dV);
1441+ // yuv.u and yuv.v are already the correct pixel_t values at the virtual target
1442+ // (show) bit depth.
14411443
1442- DrawModeColor2_DrawRect<pixel_t >(
1443- dstp, pitch, dstp_u, dstp_v, pitchUV,
1444- cx, cy, half_box, half_box,
1445- swidth, sheight,
1446- box_luma, box_u, box_v,
1447- limit_showwidth);
1444+ DrawModeColor2_DrawRect<pixel_t >(
1445+ dstp, pitch, dstp_u, dstp_v, pitchUV,
1446+ cx, cy, half_box, half_box,
1447+ swidth, sheight,
1448+ box_luma, box_u, box_v,
1449+ limit_showwidth);
14481450
1449- }
14501451 }
1452+ }
14511453
1452- // ---- Radial lines ----
1453- pixel_t line_luma;
1454- if constexpr (std::is_integral<pixel_t >::value)
1455- line_luma = (pixel_t )(120 << (bits_per_pixel - 8 ));
1456- else
1457- line_luma = c8tof (120 );
1458-
1459- // Angles to draw(matching PPro vectorscope) :
1460- // 0°, 90°, 180°, 270° — the axis cross
1461- // 33°, 123°, 213°, 303° — the ±I/±Q diagonals
1462- // knowing that in UV space these are not exactly at 33° and 123°
1463-
1464- if (params.axes ) {
1465- const double angles90[] = {
1466- 0.0 , 90.0 , 180.0 , 270.0
1467- };
1468- const int limit = (1 << (show_bits - 1 )) - 1 ;
1469- const int show_bit_shift = show_bits - 8 ;
1470- for (double ang : angles90)
1471- DrawRadialLine<pixel_t >(dstp, pitch,
1472- limit, show_bit_shift, ang, line_luma);
1473- }
1474-
1475- if (params.iq_lines ) {
1476- const double anglesIQ[] = {
1477- 33.0 , 123.0 , 213.0 , 303.0
1478- };
1479- const int limit = (1 << (show_bits - 1 )) - 1 ;
1480- const int show_bit_shift = show_bits - 8 ;
1481- for (double ang : anglesIQ)
1482- DrawRadialLine<pixel_t >(dstp, pitch,
1483- limit, show_bit_shift, ang, line_luma);
1484- }
1454+ // ---- Radial lines ----
1455+ pixel_t line_luma;
1456+ if constexpr (std::is_integral<pixel_t >::value)
1457+ line_luma = (pixel_t )(120 << (bits_per_pixel - 8 ));
1458+ else
1459+ line_luma = c8tof (120 );
1460+
1461+ // Angles to draw(matching PPro vectorscope) :
1462+ // 0°, 90°, 180°, 270° — the axis cross
1463+ // 33°, 123°, 213°, 303° — the ±I/±Q diagonals
1464+ // knowing that in UV space these are not exactly at 33° and 123°
1465+
1466+ if (params.axes ) {
1467+ const double angles90[] = {
1468+ 0.0 , 90.0 , 180.0 , 270.0
1469+ };
1470+ const int limit = (1 << (show_bits - 1 )) - 1 ;
1471+ const int show_bit_shift = show_bits - 8 ;
1472+ for (double ang : angles90)
1473+ DrawRadialLine<pixel_t >(dstp, pitch,
1474+ limit, show_bit_shift, ang, line_luma);
1475+ }
1476+
1477+ if (params.iq_lines ) {
1478+ const double anglesIQ[] = {
1479+ 33.0 , 123.0 , 213.0 , 303.0
1480+ };
1481+ const int limit = (1 << (show_bits - 1 )) - 1 ;
1482+ const int show_bit_shift = show_bits - 8 ;
1483+ for (double ang : anglesIQ)
1484+ DrawRadialLine<pixel_t >(dstp, pitch,
1485+ limit, show_bit_shift, ang, line_luma);
14851486 }
14861487}
14871488
0 commit comments