File talk:Yuv420.svg
Jump to navigation
Jump to search
Below is the source for the program I wrote to generate this thing. I got a bit carried away because I wanted to learn some more Cairo, so it probably would have been quicker to do this by hand, but at least the grids are nice and regular. --Qef (talk) 08:14, 24 June 2008 (UTC)
#!/usr/bin/perl
use warnings;
use strict;
use Cairo;
use Math::Trig; # for deg2rad
my $TEST_MODE = 0;
my @TEXT_COLOUR = (0, 0, 0);
my @GRID_COLOUR = (0, 0, 0);
my @ARROW_COLOUR = (0, 0, 0);
my %BYTE_COLOUR_SPEC = (
blue => [ 0.6, 0.8, 1 ],
green => [ 0.36, 0.52, 0.15 ],
yellow => [ 1, 1, 0.6 ],
red => [ 1, 0.4, 0.2 ],
purple => [ 0.6, 0.6, 1 ],
orange => [ 1, 0.71, 0.08 ],
);
my $FONT = 'sans';
my $BYTE_STREAM_FONT_SIZE = 9;
my $MAIN_BOX_FONT_SIZE = 16;
my $HEADINGS_FONT_SIZE = 20;
my $MARGIN = 20.5;
my $MAIN_BOX_INDENT = 5 * $MARGIN;
my $MAIN_BOX_GRID_LINE_WD = 3;
my $MAIN_BOX_GRID_LINE_WD_THIN = 1;
my $MAIN_BOX_GRID_MARGIN_X = 5;
my $MAIN_BOX_GRID_MARGIN_Y = 10;
my $ARROW_LINE_WD = 1;
my ($ARROW_CURVINESS_X, $ARROW_CURVINESS_Y) = (0.5, 0.1);
my ($ARROW_RADIUS, $ARROW_SWEEP) = (15, 20);
my $BYTE_STREAM_GRID_LINE_WD = 1;
my $BYTE_STREAM_GRID_MARGIN_X = 1.5;
my $BYTE_STREAM_GRID_MARGIN_Y = 4;
my @BYTE_STREAM = ((map "Y$_", 1 .. 24),
(map "U$_", 1 .. 6),
(map "V$_", 1 .. 6));
my %BYTE_COLOUR = qw(
Y1 blue Y2 blue Y7 blue Y8 blue U1 blue V1 blue
Y3 green Y4 green Y9 green Y10 green U2 green V2 green
Y5 yellow Y6 yellow Y11 yellow Y12 yellow U3 yellow V3 yellow
Y13 red Y14 red Y19 red Y20 red U4 red V4 red
Y15 purple Y16 purple Y21 purple Y22 purple U5 purple V5 purple
Y17 orange Y18 orange Y23 orange Y24 orange U6 orange V6 orange
);
die unless @BYTE_STREAM == scalar(keys %BYTE_COLOUR);
my ($IMG_WD, $IMG_HT, $BYTE_STREAM_BOX_WD, $BYTE_STREAM_BOX_HT,
$MAIN_BOX_WD, $MAIN_BOX_HT) = calculate_image_width();
sub make_surface {
my ($wd, $ht) = @_;
return $TEST_MODE ? Cairo::ImageSurface->create('rgb24', $wd, $ht)
: Cairo::SvgSurface->create('Yuv420.svg', $wd, $ht);
}
my $surface = make_surface($IMG_WD, $IMG_HT);
my $cr = Cairo::Context->create($surface);
# white background
if ($TEST_MODE) {
$cr->set_source_rgb(1, 1, 1);
$cr->rectangle(0, 0, $IMG_WD, $IMG_HT);
$cr->fill;
}
my $cur_y = $MARGIN;
# Main box of byte names.
$cur_y = heading($cr, $cur_y, 'Single Frame YUV420:');
{
set_font($cr, $FONT, $MAIN_BOX_FONT_SIZE);
my $n = 0;
for my $byte (@BYTE_STREAM) {
my ($x, $y) = ($n % 6, int($n / 6));
my ($boxx, $boxy) = ($MAIN_BOX_INDENT + $x * $MAIN_BOX_WD,
$cur_y + $y * $MAIN_BOX_HT);
# background colour
$cr->set_source_rgb(@{$BYTE_COLOUR_SPEC{$BYTE_COLOUR{$byte}}});
$cr->rectangle($boxx, $boxy, $MAIN_BOX_WD, $MAIN_BOX_HT);
$cr->fill;
# label
my $extents = $cr->text_extents($byte);
$cr->move_to($boxx + $MAIN_BOX_WD / 2 - $extents->{width} / 2 - $extents->{x_bearing},
$boxy + $MAIN_BOX_HT / 2 - $extents->{height} / 2 - $extents->{y_bearing});
$cr->set_source_rgb(@TEXT_COLOUR);
$cr->show_text($byte);
++$n;
}
# grid of lines inside box
$cr->set_source_rgb(@GRID_COLOUR);
for my $x (1 .. 5) {
for my $y (1 .. 5) {
# top vertical grid line (sometimes thick, sometimes thin)
$cr->set_line_width(($x % 2 == 0) ? $MAIN_BOX_GRID_LINE_WD
: $MAIN_BOX_GRID_LINE_WD_THIN);
$cr->move_to($MAIN_BOX_INDENT + $x * $MAIN_BOX_WD, $cur_y);
$cr->line_to($MAIN_BOX_INDENT + $x * $MAIN_BOX_WD,
$cur_y + 4 * $MAIN_BOX_HT);
$cr->stroke;
# bottom vertical grid line (always thick)
$cr->set_line_width($MAIN_BOX_GRID_LINE_WD);
$cr->move_to($MAIN_BOX_INDENT + $x * $MAIN_BOX_WD,
$cur_y + 4 * $MAIN_BOX_HT);
$cr->line_to($MAIN_BOX_INDENT + $x * $MAIN_BOX_WD,
$cur_y + 6 * $MAIN_BOX_HT);
$cr->stroke;
# horizontal grid line
$cr->set_line_width(($y == 2 || $y == 4 || $y == 5)
? $MAIN_BOX_GRID_LINE_WD : $MAIN_BOX_GRID_LINE_WD_THIN);
$cr->move_to($MAIN_BOX_INDENT, $cur_y + $y * $MAIN_BOX_HT);
$cr->line_to($MAIN_BOX_INDENT + 6 * $MAIN_BOX_WD,
$cur_y + $y * $MAIN_BOX_HT);
$cr->stroke;
}
}
# outside of box
$cr->set_source_rgb(@GRID_COLOUR);
$cr->set_line_width($MAIN_BOX_GRID_LINE_WD);
$cr->rectangle($MAIN_BOX_INDENT, $cur_y,
6 * $MAIN_BOX_WD, 6 * $MAIN_BOX_HT);
$cr->stroke;
# curvy lines with arrows on
arrow_line($cr, $MAIN_BOX_INDENT, $cur_y + $MAIN_BOX_HT,
$cur_y + 4.5 * $MAIN_BOX_HT, -1, 160, 200);
arrow_line($cr, $MAIN_BOX_INDENT, $cur_y + $MAIN_BOX_HT,
$cur_y + 5.5 * $MAIN_BOX_HT, -1, undef, 200);
arrow_line($cr, $MAIN_BOX_INDENT + 6 * $MAIN_BOX_WD,
$cur_y + 3 * $MAIN_BOX_HT, $cur_y + 4.5 * $MAIN_BOX_HT, +1,
30, 325);
arrow_line($cr, $MAIN_BOX_INDENT + 6 * $MAIN_BOX_WD,
$cur_y + 3 * $MAIN_BOX_HT, $cur_y + 5.5 * $MAIN_BOX_HT, +1,
undef, 335);
$cur_y += $MAIN_BOX_HT * 6 + $MARGIN;
}
# order of byte stream at bottom of picture.
$cur_y = heading($cr, $cur_y, 'Position in byte stream:');
{
set_font($cr, $FONT, $BYTE_STREAM_FONT_SIZE);
my $x = $MARGIN;
my $y = $cur_y;
for my $byte (@BYTE_STREAM) {
# background
$cr->set_source_rgb(@{$BYTE_COLOUR_SPEC{$BYTE_COLOUR{$byte}}});
$cr->rectangle($x, $y, $BYTE_STREAM_BOX_WD, $BYTE_STREAM_BOX_HT);
$cr->fill;
# label
my $extents = $cr->text_extents($byte);
$cr->move_to($x + $BYTE_STREAM_BOX_WD / 2 - $extents->{width} / 2 - $extents->{x_bearing},
$y + $BYTE_STREAM_BOX_HT / 2 - $extents->{height} / 2 - $extents->{y_bearing});
$cr->set_source_rgb(@TEXT_COLOUR);
$cr->show_text($byte);
# line to left
if ($byte ne 'Y1') {
$cr->move_to($x, $y);
$cr->line_to($x, $y + $BYTE_STREAM_BOX_HT);
$cr->set_source_rgb(@GRID_COLOUR);
$cr->set_line_width($BYTE_STREAM_GRID_LINE_WD);
$cr->stroke;
}
$x += $BYTE_STREAM_BOX_WD;
}
# box round outside
$cr->rectangle($MARGIN, $y, $IMG_WD - 2 * $MARGIN, $BYTE_STREAM_BOX_HT);
$cr->set_source_rgb(@GRID_COLOUR);
$cr->set_line_width($BYTE_STREAM_GRID_LINE_WD);
$cr->stroke;
}
$cr->show_page;
$surface->write_to_png('Yuv420.png') if $TEST_MODE;
sub set_font {
my ($cr, $face, $size) = @_;
$cr->select_font_face($face, 'normal', 'normal');
$cr->set_font_size($size);
}
sub heading {
my ($cr, $y, $text) = @_;
set_font($cr, $FONT, $HEADINGS_FONT_SIZE);
my $extents = $cr->text_extents($text);
$cr->move_to($MARGIN + $extents->{x_bearing}, $y - $extents->{y_bearing});
$cr->set_source_rgb(@TEXT_COLOUR);
$cr->show_text($text);
return $y + $extents->{height} + $MARGIN / 2;
}
sub draw_arrow {
my ($cr, $x, $y, $a) = @_;
$cr->move_to($x, $y);
$cr->line_to($x + $ARROW_RADIUS * cos(deg2rad($a + $ARROW_SWEEP)),
$y + $ARROW_RADIUS * sin(deg2rad($a + $ARROW_SWEEP)));
$cr->line_to($x + $ARROW_RADIUS * cos(deg2rad($a - $ARROW_SWEEP)),
$y + $ARROW_RADIUS * sin(deg2rad($a - $ARROW_SWEEP)));
$cr->close_path;
$cr->set_source_rgb(@ARROW_COLOUR);
$cr->fill;
}
sub arrow_line {
my ($cr, $x, $y1, $y2, $dir, $ang1, $ang2) = @_;
my $dist = $y2 - $y1;
$cr->move_to($x, $y1);
$cr->curve_to($x + $ARROW_CURVINESS_X * $dir * $dist,
$y1 + $ARROW_CURVINESS_Y * $dist,
$x + $ARROW_CURVINESS_X * $dir * $dist,
$y2 - $ARROW_CURVINESS_Y * $dist,
$x, $y2);
$cr->set_source_rgb(@ARROW_COLOUR);
$cr->set_line_width($ARROW_LINE_WD);
$cr->stroke;
draw_arrow($cr, $x, $y1, $ang1) if defined $ang1;
draw_arrow($cr, $x, $y2, $ang2) if defined $ang2;
}
sub calculate_image_width {
# create a temporary context for getting text widths.
my $surface = make_surface(100, 100);
my $cr = Cairo::Context->create($surface);
# find size of widest and highest of the byte names.
my ($maxwd, $maxht) = (0, 0);
my ($mainmaxwd, $mainmaxht) = (0, 0);
for (@BYTE_STREAM) {
set_font($cr, $FONT, $BYTE_STREAM_FONT_SIZE);
my $extents = $cr->text_extents($_);
$maxwd = $extents->{width} if $extents->{width} > $maxwd;
$maxht = $extents->{height} if $extents->{height} > $maxht;
set_font($cr, $FONT, $MAIN_BOX_FONT_SIZE);
$extents = $cr->text_extents($_);
$mainmaxwd = $extents->{width} if $extents->{width} > $mainmaxwd;
$mainmaxht = $extents->{height} if $extents->{height} > $mainmaxht;
}
my $byte_box_wd = $maxwd + $BYTE_STREAM_GRID_LINE_WD +
2 * $BYTE_STREAM_GRID_MARGIN_X;
my $byte_box_ht = $maxht + 2 * $BYTE_STREAM_GRID_MARGIN_Y +
$BYTE_STREAM_GRID_LINE_WD;
my $main_box_wd = $mainmaxwd + $MAIN_BOX_GRID_LINE_WD +
2 * $MAIN_BOX_GRID_MARGIN_X;
my $main_box_ht = $mainmaxht + 2 * $MAIN_BOX_GRID_MARGIN_Y +
$MAIN_BOX_GRID_LINE_WD;
my $imgwd = 2 * $MARGIN + $byte_box_wd * @BYTE_STREAM;
return ($imgwd, 4 * $MARGIN + 6 * $main_box_ht + $byte_box_ht + 2 * $HEADINGS_FONT_SIZE,
$byte_box_wd, $byte_box_ht, $main_box_wd, $main_box_ht);
}