#!/usr/bin/perl -w

use strict;

use Graphics::Framebuffer;
use List::Util qw(min max shuffle);
use Time::HiRes qw(sleep time);
# use Data::Dumper::Simple; $Data::Dumper::Sortkeys=1;

system('clear');
$| = 1;

my $file = 0;
my $arg  = join(' ', @ARGV);
my $new_x;
my $new_y;
my $F;
my $dev = 0;

$file = 1 if ($arg =~ /file/i);    # File Handle drawing mode

if ($arg =~ /X(\d+)/) {
    $new_x = $1;
    $arg =~ s/X\d+//;
}
if ($arg =~ /Y(\d+)/) {
    $new_y = $1;
    $arg =~ s/Y\d+//;
}
if ($arg =~ /(\d+)/) {
    $dev = $1;
}

if (defined($new_x)) {
    $F = Graphics::Framebuffer->new('FB_DEVICE' => "/dev/fb$dev",'FILE_MODE' => $file, 'SHOW_ERRORS' => 0, 'SIMULATED_X' => $new_x,'SIMULATED_Y' => $new_y);
} else {
    $F = Graphics::Framebuffer->new('FB_DEVICE' => "/dev/fb$dev",'FILE_MODE' => $file, 'SHOW_ERRORS' => 0);
}

$SIG{'QUIT'} = sub {$F->clip_reset();$F->cls('ON'); exit(0);};
$SIG{'INT'}  = sub {$F->clip_reset();$F->cls('ON'); exit(0);};

my $screen_width  = $F->{'VXRES'};
my $screen_height = $F->{'VYRES'};
my $xm            = $F->{'VXRES'} / 1920;
my $ym            = $F->{'VYRES'} / 1080;
my $XX            = $screen_width;
my $YY            = $screen_height;
my $center_x      = $XX / 2;
my $center_y      = $YY / 2;
my $factor        = 5;
my $images_path   = (-e 'images/GFB.png') ? 'images' : 'examples/images';
my $rpi           = ($F->{'fscreeninfo'}->{'id'} =~ /BCM270(8|9)/i) ? 1 : 0;

$F->cls('OFF');

# if (0) {
color_mapping();
plotting();
lines(0);
lines(1);
angle_lines(0);
angle_lines(1);
boxes();
filled_boxes();
vertical_gradient_boxes();
horizontal_gradient_boxes();
rounded_boxes();
filled_rounded_boxes();
vertical_gradient_rounded_boxes();
horizontal_gradient_rounded_boxes();
circles();
filled_circles();
vertical_gradient_circles();
horizontal_gradient_circles();
arcs();
poly_arcs();
filled_pies();
vertical_gradient_pies();
# horizontal_gradient_pies();
ellipses();
filled_ellipses();
vertical_gradient_ellipses();
horizontal_gradient_ellipses();
polygons(0);
polygons(1);
filled_polygons();
vertical_gradient_polygons();
# horizontal_gradient_polygons();
beziers();
truetype_fonts();
flood_fill();
color_replace_with_clipping();
color_replace_no_clipping();
image_load();
blitting();
rotate();
monochrome(flipping());
animated();
or_drawing();
xor_drawing();
and_drawing();
mask_drawing();
unmask_drawing();

$F->clip_reset();
$F->cls('ON');

exit(0);

sub color_mapping {
    print_it($F,'Color Mapping Test -> Red-Green-Blue');

    $F->rbox(
        {
            'filled' => 1,
            'x'      => 0,
            'y'      => 0,
            'width'  => $XX,
            'height' => $YY,
            'gradient' => {
                'direction' => 'horizontal',
                'colors' => {
                    'red'   => [255,0,0],
                    'green' => [0,255,0],
                    'blue'  => [0,0,255]
                }
            }
        }
    );

    my $image = $F->load_image(
        {
            'x'      => 0,
            'y'      => 0,
            'width'  => $XX * .75,
            'height' => ($YY - $F->{'Y_CLIP'}) * .75,
            'center' => 3,
            'file'   => "$images_path/testpattern.jpg"
        }
    );
    $F->blit_write($image);
    sleep $factor;
}

sub plotting {
    print_it($F, 'Plotting Test');

    my $s = time + $factor;
    while (time < $s) {
        my $x = int(rand($screen_width));
        my $y = int(rand($screen_height));
        $F->set_color({'alpha' => 255, 'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->plot({'x' => $x, 'y' => $y});
    }
}

sub lines {
    my $aa = shift;
    if ($aa) {
        print_it($F, 'Antialiased Line Test');
    } else {
        print_it($F, 'Line Test');
    }
    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->line({'x' => rand($XX), 'y' => rand($YY), 'xx' => rand($XX), 'yy' => rand($YY),'antialiased'=>$aa});
    }
}

sub angle_lines {
    my $aa = shift;
    if ($aa) {
        print_it($F, 'Antialiased Angle Line Test');
    } else {
        print_it($F, 'Angle Line Test');
    }

    my $s = time + $factor;
    my $angle = 0;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->angle_line({'x' => $center_x, 'y' => $center_y, 'radius' => ($center_y - $F->{'Y_CLIP'}), 'angle' => $angle,'antialiased'=>$aa});
        $angle += 1.1;
        $angle -= 360 if ($angle >= 360);
    }
}

sub boxes {
    print_it($F, 'Box Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->box({'x' => rand($XX), 'y' => rand($YY), 'xx' => rand($XX), 'yy' => rand($YY)});
    }
}

sub filled_boxes {
    print_it($F, 'Filled Box Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->box({'x' => rand($XX), 'y' => rand($YY), 'xx' => rand($XX), 'yy' => rand($YY), 'filled' => 1});
    }
}

sub vertical_gradient_boxes {
    print_it($F, 'Vertical Gradient Box Test');
    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->box(
            {
                'x'        => rand($XX),
                'y'        => rand($YY),
                'xx'       => rand($XX),
                'yy'       => rand($YY),
                'filled'   => 1,
                'gradient' => {
                    'colors' => {
                        'red' => [rand(256),rand(256),rand(256)],
                        'green' => [rand(256),rand(256),rand(256)],
                        'blue' => [rand(256),rand(256),rand(256)],
                    }
                }
            }
        );
    }
}

sub horizontal_gradient_boxes {
    print_it($F, 'Horizontal Gradient Box Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->box(
            {
                'x'        => rand($XX),
                'y'        => rand($YY),
                'xx'       => rand($XX),
                'yy'       => rand($YY),
                'filled'   => 1,
                'gradient' => {
                    'direction' => 'horizontal',
                    'colors' => {
                        'red' => [rand(256),rand(256),rand(256)],
                        'green' => [rand(256),rand(256),rand(256)],
                        'blue' => [rand(256),rand(256),rand(256)],
                    }
                }
            }
        );
    }
}

sub rounded_boxes {
    print_it($F, 'Rounded Box Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->box({'x' => rand($XX), 'y' => rand($YY), 'xx' => rand($XX), 'yy' => rand($YY), 'radius' => rand($XX / 16)});
    }
}

sub filled_rounded_boxes {
    print_it($F, 'Filled Rounded Box Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->box({'x' => rand($XX), 'y' => rand($YY), 'xx' => rand($XX), 'yy' => rand($YY), 'radius' => rand($XX / 16), 'filled' => 1});
    }
}

sub vertical_gradient_rounded_boxes {
    print_it($F, 'Vertical Gradient Rounded Box Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->box(
            {
                'x'        => rand($XX),
                'y'        => rand($YY),
                'xx'       => rand($XX),
                'yy'       => rand($YY),
                'radius'   => rand($XX / 16),
                'filled'   => 1,
                'gradient' => {
                    'colors' => {
                        'red' => [rand(256),rand(256),rand(256)],
                        'green' => [rand(256),rand(256),rand(256)],
                        'blue' => [rand(256),rand(256),rand(256)],
                    }
                }
            }
        );
    }
}

sub horizontal_gradient_rounded_boxes {
    print_it($F, 'Horizontal Gradient Rounded Box Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->box(
            {
                'x'        => rand($XX),
                'y'        => rand($YY),
                'xx'       => rand($XX),
                'yy'       => rand($YY),
                'radius'   => rand($XX / 16),
                'filled'   => 1,
                'gradient' => {
                    'direction' => 'horizontal',
                    'colors' => {
                        'red' => [rand(256),rand(256),rand(256)],
                        'green' => [rand(256),rand(256),rand(256)],
                        'blue' => [rand(256),rand(256),rand(256)],
                    }
                }
            }
        );
    }
}

sub circles {
    print_it($F, 'Circle Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->circle({'x' => rand($XX), 'y' => rand($YY), 'radius' => rand($center_y)});
    }
}

sub filled_circles {
    print_it($F, 'Filled Circle Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->circle({'x' => rand($XX), 'y' => rand($YY), 'radius' => rand($center_y), 'filled' => 1});
    }
}

sub vertical_gradient_circles {
    print_it($F, 'Vertical Gradient Circle Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->circle(
            {
                'x'        => rand($XX),
                'y'        => rand($YY),
                'radius'   => rand($center_y),
                'filled'   => 1,
                'gradient' => {
                    'colors' => {
                        'red' => [rand(256),rand(256),rand(256)],
                        'green' => [rand(256),rand(256),rand(256)],
                        'blue' => [rand(256),rand(256),rand(256)],
                    }
                }
            }
        );
    }
}

sub horizontal_gradient_circles {
    print_it($F, 'Horizontal Gradient Circle Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->circle(
            {
                'x'        => rand($XX),
                'y'        => rand($YY),
                'radius'   => rand($center_y),
                'filled'   => 1,
                'gradient' => {
                    'direction' => 'horizontal',
                    'colors' => {
                        'red' => [rand(256),rand(256),rand(256)],
                        'green' => [rand(256),rand(256),rand(256)],
                        'blue' => [rand(256),rand(256),rand(256)],
                    }
                }
            }
        );
    }
}

sub arcs {
    print_it($F, 'Arc Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->arc({'x' => rand($XX), 'y' => rand($YY), 'radius' => rand($center_y), 'start_degrees' => rand(360), 'end_degrees' => rand(360)});
    }
}

sub poly_arcs {
    print_it($F, 'Poly Arc Test');
    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->poly_arc({'x' => rand($XX), 'y' => rand($YY), 'radius' => rand($center_y), 'start_degrees' => rand(360), 'end_degrees' => rand(360)});
    }
}

sub filled_pies {
    print_it($F, 'Filled Pie Test');

    my $s = time + ($factor * 2);
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->filled_pie({'x' => rand($XX), 'y' => rand($YY), 'radius' => rand($center_y), 'start_degrees' => rand(360), 'end_degrees' => rand(360)});
    }
}

sub vertical_gradient_pies {
    print_it($F, 'Vertical Gradient Filled Pie Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->filled_pie(
            {
                'x'             => rand($XX),
                'y'             => rand($YY),
                'radius'        => rand($center_y),
                'start_degrees' => rand(360),
                'end_degrees'   => rand(360),
                'gradient'      => {
                    'colors' => {
                        'red' => [rand(256),rand(256),rand(256)],
                        'green' => [rand(256),rand(256),rand(256)],
                        'blue' => [rand(256),rand(256),rand(256)],
                    }
                }
            }
        );
    }
}

sub horizontal_gradient_pies {
    print_it($F, 'Horizontal Gradient Filled Pie Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->filled_pie(
            {
                'x'             => rand($XX),
                'y'             => rand($YY),
                'radius'        => rand($center_y),
                'start_degrees' => rand(360),
                'end_degrees'   => rand(360),
                'gradient'      => {
                    'direction' => 'horizontal',
                    'colors'    => {
                        'red' => [rand(256),rand(256),rand(256)],
                        'green' => [rand(256),rand(256),rand(256)],
                        'blue' => [rand(256),rand(256),rand(256)],
                    }
                }
            }
        );
    }
}

sub ellipses {
    print_it($F, 'Ellipse Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->ellipse({'x' => rand($XX), 'y' => rand($YY), 'xradius' => rand($center_x), 'yradius' => rand($center_y)});
    }
}

sub filled_ellipses {
    print_it($F, 'Filled Ellipse Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->ellipse({'x' => rand($XX), 'y' => rand($YY), 'xradius' => rand($center_x), 'yradius' => rand($center_y), 'filled' => 1});
    }
}

sub vertical_gradient_ellipses {
    print_it($F, 'Vertical Gradient Ellipse Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->ellipse(
            {
                'x'        => rand($XX),
                'y'        => rand($YY),
                'xradius'  => rand($center_x),
                'yradius'  => rand($center_y),
                'filled'   => 1,
                'gradient' => {
                    'colors' => {
                        'red' => [rand(256),rand(256),rand(256)],
                        'green' => [rand(256),rand(256),rand(256)],
                        'blue' => [rand(256),rand(256),rand(256)],
                    }
                }
            }
        );
    }
}

sub horizontal_gradient_ellipses {
    print_it($F, 'Horizontal Gradient Ellipse Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->ellipse(
            {
                'x'        => rand($XX),
                'y'        => rand($YY),
                'xradius'  => rand($center_x),
                'yradius'  => rand($center_y),
                'filled'   => 1,
                'gradient' => {
                    'direction' => 'horizontal',
                    'colors' => {
                        'red' => [rand(256),rand(256),rand(256)],
                        'green' => [rand(256),rand(256),rand(256)],
                        'blue' => [rand(256),rand(256),rand(256)],
                    }
                }
            }
        );
    }
}

sub polygons {
    my $aa = shift;
    if ($aa) {
        print_it($F, 'Antialiased Polygon Test');
    } else {
        print_it($F, 'Polygon Test');
    }

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->polygon({'coordinates' => [rand($XX), rand($YY), rand($XX), rand($YY), rand($XX), rand($YY), rand($XX), rand($YY)],'antialiased' => $aa});
    }
}

sub filled_polygons {
    print_it($F, 'Filled Polygon Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->polygon({'coordinates' => [rand($XX), rand($YY), rand($XX), rand($YY), rand($XX), rand($YY), rand($XX), rand($YY)], 'filled' => 1});
    }
}

sub vertical_gradient_polygons {
    print_it($F, 'Vertical Gradient Polygon Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->polygon(
            {
                'coordinates' => [rand($XX), rand($YY), rand($XX), rand($YY), rand($XX), rand($YY), rand($XX), rand($YY)],
                'filled'      => 1,
                'gradient'    => {
                    'colors' => {
                        'red' => [rand(256),rand(256),rand(256)],
                        'green' => [rand(256),rand(256),rand(256)],
                        'blue' => [rand(256),rand(256),rand(256)],
                    }
                }
            }
        );
    }
}

sub horizontal_gradient_polygons {
    print_it($F, 'Horizontal Gradient Polygon Test');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->polygon(
            {
                'coordinates' => [rand($XX), rand($YY), rand($XX), rand($YY), rand($XX), rand($YY), rand($XX), rand($YY)],
                'filled'      => 1,
                'gradient'    => {
                    'direction' => 'horizontal',
                    'colors' => {
                        'red' => [rand(256),rand(256),rand(256)],
                        'green' => [rand(256),rand(256),rand(256)],
                        'blue' => [rand(256),rand(256),rand(256)],
                    }
                }
            }
        );
    }
}

sub beziers {
    print_it($F, 'Testing Bezier Curves');

    my $s = time + $factor;
    while (time < $s) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        my @coords = ();
        foreach my $c (1 .. int(rand(20))) {
            push(@coords, int(rand($XX)), int(rand($YY)));
        }
        $F->bezier({'coordinates' => \@coords, 'points' => 100});
    }
}

sub truetype_fonts {
    print_it($F, 'Testing TrueType Font Rendering (random height/width)');

    my @fonts = (keys %{$F->{'FONTS'}});
    my $g = time + $factor;
    while (time < $g) {
        my $x  = int(rand(600 * $xm));
        my $y  = int(rand(1080 * $ym));
        my $h  = ($YY <= 240 || $F->{'BITS'} == 16) ? (6 + rand(60)) : (8 + int(rand(300 * $ym)));
        my $ws = ($XX <= 320 || $F->{'BITS'} == 16) ? rand(2) : rand(4);
        my $font = $fonts[int(rand(scalar(@fonts)))];
        my $b = $F->ttf_print(
            {
                'x'            => $x,
                'y'            => $y,
                'height'       => $h,
                'wscale'       => $ws,
                'color'        => sprintf('%02x%02x%02x%02x', int(rand(256)), int(rand(256)), int(rand(256)),255),
                'text'         => $font,
                'font_path'    => $F->{'FONTS'}->{$font}->{'path'},
                'face'         => $F->{'FONTS'}->{$font}->{'font'},
                'bounding_box' => 1,
#                'center'       => 3
            }
        );
        if (defined($b)) {
            $b->{'x'} = rand($F->{'XX_CLIP'} - $b->{'pwidth'});
            $F->ttf_print($b);
        }
    }
}

sub flood_fill {
    print_it($F, 'Testing flood fill');
    if ($XX > 255) {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->polygon({'coordinates' => [220 * $xm, 190 * $ym, 1520 * $xm, 80 * $xm, 1160 * $xm, $YY, 960 * $xm, 540 * $ym, 760 * $xm, 780 * $ym]});
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->polygon({'coordinates' => [1270 * $xm, 570 * $ym, 970 * $xm, 170 * $ym, 600 * $xm, 500 * $ym]});
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->circle({'x' => 500 * $xm, 'y' => 320 * $ym, 'radius' => 100 * $xm});

        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->fill({'x' => 240 * $xm, 'y' => 200 * $ym});

        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->fill({'x' => 960 * $xm, 'y' => 440 * $ym});
    } else {
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->polygon({'coordinates' => [$center_x, 3, 3, $YY - 3, $center_x, $center_y, $XX - 3, $YY - 4]});
        $F->set_color({'red' => rand(256), 'green' => rand(256), 'blue' => rand(256)});
        $F->fill({'x' => 3, 'y' => 3});
    }
}

sub color_replace_with_clipping {
    print_it($F, 'Testing Color Replace (clipping on)');

    my $x = $F->{'XRES'} / 4;
    my $y = $F->{'YRES'} / 4;
    $F->splash();
    $F->clip_set({'x' => $x, 'y' => $y, 'xx' => $x * 3, 'yy' => $y * 3});
    my $r = 128;
    my $g = 0;
    my $b = 0;
    my $s = time + $factor;
    while (time < $s) {
        my $R = int(rand(256));
        my $G = int(rand(256));
        my $B = int(rand(256));
        $F->replace_color(
            {
                'old' => {
                    'red'   => $r,
                    'green' => $g,
                    'blue'  => $b
                },
                'new' => {
                    'red'   => $R,
                    'green' => $G,
                    'blue'  => $B
                }
            }
        );
        ($r, $g, $b) = ($R, $G, $B);
    }
}

sub color_replace_no_clipping {
    print_it($F, 'Testing Color Replace (clipping off)');

    $F->splash();

    $F->clip_reset();

    my $r = 128;
    my $g = 0;
    my $b = 0;
    my $s = time + $factor;
    while (time < $s) {
        my $R = int(rand(256));
        my $G = int(rand(256));
        my $B = int(rand(256));
        $F->replace_color(
            {
                'old' => {
                    'red'   => $r,
                    'green' => $g,
                    'blue'  => $b
                },
                'new' => {
                    'red'   => $R,
                    'green' => $G,
                    'blue'  => $B
                }
            }
        );
        ($r, $g, $b) = ($R, $G, $B);
    }
}

sub image_load {
    print_it($F, 'Testing Image load (different sizes)');

    my $blank;
    my $s = time + $factor;
    while (time < $s) {
        my $image = $F->load_image(
            {
                'x'      => 0,
                'y'      => 0,
                'width'  => rand($XX),
                'height' => rand($YY - $F->{'Y_CLIP'}),
                'center' => 3,
                'file'   => "$images_path/GFB.png"
            }
        );
        if (defined($blank)) {
            $F->xor_mode();
            $F->blit_write($blank);
            $F->normal_mode();
        }
        $F->blit_write($image);
        %{$blank} = %{$image};
    }
    my $image = $F->load_image(
        {
            'x'      => 0,
            'y'      => 0,
            'width'  => $XX,
            'height' => ($YY - $F->{'Y_CLIP'}),
            'center' => 3,
            'file'   => "$images_path/GFB.png"
        }
    );
    if (defined($blank)) {
        $F->xor_mode();
        $F->blit_write($blank);
        $F->normal_mode();
    }
    $F->blit_write($image);
    sleep $factor;
}

sub blitting {
    print_it($F, 'Testing Image blitting');

    my $image = $F->load_image(
        {
            'x'      => 0,
            'y'      => 0,
            'width'  => int($XX/3),
            'height' => int($YY/3),
            'file'   => "$images_path/RWBY_Logo.png"
        }
    );
    my $s = time + $factor;
    while (time < $s) {
        $image->{'x'} = abs(rand($XX - $image->{'width'}));
        $image->{'y'} = $F->{'Y_CLIP'} + abs(rand(($YY - $F->{'Y_CLIP'}) - $image->{'height'}));
        $F->blit_write($image);
    }
}

sub rotate {
    print_it($F, 'Loading "RWBY_Oh_No.jpg" for Rotate Test','FFFF00FF');
    my $image;
    if ($YY<=240) {
        $image = $F->load_image(
            {
                'x'      => 0,
                'y'      => 0,
                'width'  => int($YY*.7),
                'file'   => "$images_path/RWBY_Oh_No.jpg"
            }
        );
    } elsif ($rpi) {
        $image = $F->load_image(
            {
                'x'      => 0,
                'y'      => 0,
                'width'  => 160,
                'height' => 120,
                'file'   => "$images_path/RWBY_Oh_No.jpg"
            }
        );
    } else {
        $image = $F->load_image(
            {
                'x'      => 0,
                'y'      => 0,
                'width'  => int($XX * .4),
                'height' => int($YY * .4),
                'file'   => "$images_path/RWBY_Oh_No.jpg"
            }
        );
    }
    my $angle = 0;
    
    print_it($F, 'Counter-Clockwise Rotate Test');
    my $s = time + ($factor * 2);
    my $count = 0;
    while (time < $s || $count < 6) {
        my $rot = $F->blit_transform(
            {
                'rotate' => {
                    'degrees' => $angle
                },
                'blit_data' => $image
            }
        );
        $rot->{'x'} = abs(($XX - $rot->{'width'})  / 2);
        $rot->{'y'} = $F->{'Y_CLIP'} + abs((($YY - $F->{'Y_CLIP'}) - $rot->{'height'}) / 2);

        $F->blit_write($rot);

        $angle += 5;
        $angle = 0 if ($angle >= 360);
        $count++;
    }

    $angle = 0;

    print_it($F, 'Clockwise Rotate Test');
    $s = time + ($factor * 2);
    $count = 0;
    while (time < $s || $count < 6) {
        my $rot = $F->blit_transform(
            {
                'rotate' => {
                    'degrees' => $angle
                },
                'blit_data' => $image
            }
        );
        $rot->{'x'} = abs(($XX - $rot->{'width'})  / 2);
        $rot->{'y'} = $F->{'Y_CLIP'} + abs((($YY - $F->{'Y_CLIP'}) - $rot->{'height'}) / 2);

        $F->blit_write($rot);

        $angle -= 5;
        $angle = 0 if ($angle <= -360);
        $count++;
    }
}

sub flipping {
    print_it($F, 'Loading "RWBY_Faces.png" For Image Flip Tests','FFFF00FF');

    my $image;
    if ($rpi && $XX > 320) {
        $image = $F->load_image(
            {
                'x'    => 0,
                'y'    => $F->{'Y_CLIP'},
                'file' => "$images_path/RWBY_Faces.png"
            }
        );
    } else {
        $image = $F->load_image(
            {
                'x'      => 0,
                'y'      => $F->{'Y_CLIP'},
                'width'  => $XX,
                'height' => ($YY - $F->{'Y_CLIP'}),
                'file'   => "$images_path/RWBY_Faces.png"
            }
        );
    }
    my $s = time + $factor;
    while (time < $s) {
        foreach my $dir (qw(normal horizontal vertical both)) {
            my $rot = $F->blit_transform(
                {
                    'flip'      => $dir,
                    'blit_data' => $image
                }
            );
            $rot->{'x'} = abs(($XX - $rot->{'width'})  / 2);
            $rot->{'y'} = abs((($YY - $F->{'Y_CLIP'}) - $rot->{'height'}) / 2);
            print_it($F, "Testing Image Flip $dir");
            $F->blit_write($rot);
            sleep .3;
        }
    }
    return($image);
}

sub monochrome {
    my $image = shift;
    print_it($F, 'Testing Monochrome Image blitting');

    if ($F->{'BITS'} == 16) {
        $image = $F->load_image(
            {
                'x'      => 0,
                'y'      => $F->{'Y_CLIP'},
                'width'  => int($XX * .6),
                'height' => int($YY * .6),
                'file'   => "$images_path/RWBY_Faces.png"
            }
        );
    } else {
        $image = $F->blit_transform(
            {
                'scale' => {
                    'width'  => int($XX * .6),
                    'height' => int($YY * .6)
                },
                'blit_data' => $image
            }
        );
    }
    $image->{'image'} = $F->monochrome({'image' => $image->{'image'}, 'bits' => $F->{'BITS'}});
    my $s = time + $factor;
    while (time < $s) {
        $image->{'x'} = abs(rand($XX - $image->{'width'}));
        $image->{'y'} = $F->{'Y_CLIP'} + abs(rand(($YY - $F->{'Y_CLIP'}) - $image->{'height'}));
        $F->blit_write($image);
    }
}

sub animated {
    print_it($F,'Testing Animated Images.  Loading...','FFFF00FF');
    opendir(my $DIR,$images_path);
    chomp(my @list = readdir($DIR));
    closedir($DIR);
    my $image;
    @list = shuffle(@list);
    foreach my $info (@list) {
        next unless ($info =~ /\.gif$/i);
        for my $count (0 .. 1) {
            if ($count || $XX <= 320) {
                print_it($F, "Loading Animated Image '$info' Scaled to Full Screen",'FFFF00FF');
                $image = $F->load_image(
                    {
                        'width'  => $XX,
                        'height' => $YY - $F->{'Y_CLIP'},
                        'file'   => "$images_path/$info",
                        'center' => 3
                    }
                );
            } else {
                print_it($F, "Loading Animated Image '$info' Native Size",'FFFF00FF');
                $image = $F->load_image(
                    {
                        'file'   => "$images_path/$info",
                        'center' => 3
                    }
                );
            }
            foreach my $bench (0 .. 1) {
                if ($count || $XX <= 320) {
                    print_it($F, $bench ? "Testing Animated Image Display of '$info' Fullscreen Benchmark" : "Testing Animated Image Display of '$info' Fullscreen");
                } else {
                    print_it($F, $bench ? "Testing Animated Image Display of '$info' Native Size Benchmark" : "Testing Animated Image Display of '$info' Native Size");
                }
                if (defined($image)) {
                    $F->cls();
                    my $fps   = 0;
                    my $start = time;
                    my $s = time + ($factor);
                    while (time <= $s) {
                        foreach my $frame (0 .. (scalar(@{$image}) - 1)) {
                            if (time > $s * 2) {
                                print_it($F, 'Your System is Too Slow To Complete The Animation','FF9999FF');
                                sleep 2;
                                last;
                            }
                            my $begin = time;
                            $F->blit_write($image->[$frame]);
                            my $delay = (($image->[$frame]->{'tags'}->{'gif_delay'} * .01)) - (time - $begin);
                            if ($delay > 0 && !$bench) {
                                sleep $delay;
                            }
                            $fps++;
                            my $end = time - $start;
                            if ($end >= 1 && $bench) {
                                print STDERR "\r",sprintf('%.02f FPS',(1 / $end) * $fps);$|=1;
                                $fps = 0;
                                $start = time;
                            }
                        }
                    }
                }
            }
            last if ($XX <= 320 || $rpi);
        }
    }
}

sub or_drawing {
    print_it($F, 'Testing OR Drawing Mode');

    $F->or_mode();
    my $size = int(($YY - $F->{'Y_CLIP'}) / 3);
    my $mid  = int($XX / 2);

    $F->set_color({'red' => 255, 'green' => 0, 'blue' => 0});
    $F->circle({'x' => $mid - ($size / 2),'y'=> $F->{'Y_CLIP'} + $size*2,'radius' => $size, 'filled' => 1});
    sleep .1;

    $F->set_color({'red' => 0, 'green' => 255, 'blue' => 0});
    $F->circle({'x' => $mid,'y'=> $F->{'Y_CLIP'} + $size,'radius' => $size, 'filled' => 1});
    sleep .1;

    $F->set_color({'red' => 0, 'green' => 0, 'blue' => 255});
    $F->circle({'x' => $mid + ($size / 2),'y'=> $F->{'Y_CLIP'} + $size * 2,'radius' => $size, 'filled' => 1});
    sleep $factor;
}

sub xor_drawing {
    print_it($F, 'Testing XOR Drawing Mode');

    $F->xor_mode();

    $F->set_color({'red' => 0, 'green' => 255, 'blue' => 255});
    $F->rbox({'x' => 0,'y' => 0, 'width'=> ($XX / 2),'height' => ($YY / 2),'filled' => 1});
    sleep .1;

    $F->set_color({'red' => 255, 'green' => 0, 'blue' => 255});
    $F->rbox({'x' => $XX / 4,'y' => $YY / 4, 'width'=> ($XX / 2),'height' => ($YY / 2),'filled' => 1});
    sleep .1;

    $F->set_color({'red' => 255, 'green' => 255, 'blue' => 0});
    $F->rbox({'x' => ($XX / 2),'y' => ($YY / 2), 'width'=> ($XX / 2),'height' => ($YY / 2),'filled' => 1});
    sleep $factor;
}

sub and_drawing {
    print_it($F, 'Testing AND Drawing Mode');

    $F->set_color({'red' => 255, 'green' => 255, 'blue' => 255});
    $F->box({'x' => 0, 'y' => $F->{'Y_CLIP'}, 'xx' => $XX,'yy' => $YY, 'filled' => 1});
    $F->and_mode();
    my $size = int(($YY - $F->{'Y_CLIP'}) / 3);
    my $mid  = int($XX / 2);

    sleep .1;
    $F->set_color({'red' => 255, 'green' => 0, 'blue' => 0});
    $F->circle({'x' => $mid - ($size / 2),'y'=> $F->{'Y_CLIP'} + $size*2,'radius' => $size, 'filled' => 1});
    sleep .1;

    $F->set_color({'red' => 0, 'green' => 255, 'blue' => 0});
    $F->circle({'x' => $mid,'y'=> $F->{'Y_CLIP'} + $size,'radius' => $size, 'filled' => 1});
    sleep .1;

    $F->set_color({'red' => 0, 'green' => 0, 'blue' => 255});
    $F->circle({'x' => $mid + ($size / 2),'y'=> $F->{'Y_CLIP'} + $size * 2,'radius' => $size, 'filled' => 1});
    sleep $factor;
}

sub mask_drawing {
    print_it($F, 'Testing MASK Drawing Mode (This is SLOOOOW)');
    my $h = int($YY - $F->{'Y_CLIP'});
    my $image1 = $F->load_image(
        {
            'x'      => 0,
            'y'      => $F->{'Y_CLIP'},
            'width'  => ($XX<=320) ? $XX : $XX/2,
            'height' => ($XX<=320) ? $h : $h/2,
            'file'   => "$images_path/GFB.png",
            'center' => 3
        }
    );
    my $image2 = $F->load_image(
        {
            'x'      => 0,
            'y'      => $F->{'Y_CLIP'},
            'width'  => ($XX<=320) ? $XX : $XX/2,
            'height' => ($XX<=320) ? $h : $h/2,
            'file'   => "$images_path/RWBY_beuowolf.jpg",
            'center' => 3,
            'convertalpha' => 1
        }
    );
    $F->blit_write($image1);
    $F->mask_mode();
    $F->blit_write($image2);
    $image1 = $F->load_image(
        {
            'x'      => 0,
            'y'      => $F->{'Y_CLIP'},
            'width'  => $XX,
            'height' => $h,
            'file'   => "$images_path/GFB.png",
            'center' => 3
        }
    );
    $image2 = $F->load_image(
        {
            'x'      => 0,
            'y'      => $F->{'Y_CLIP'},
            'width'  => $XX,
            'height' => $h,
            'file'   => "$images_path/RWBY_beuowolf.jpg",
            'center' => 3,
            'convertalpha' => 1
        }
    );
  
    if ($F->{'BITS'} > 16) {
        print_it($F,'Simulating MASK Mode with the much quicker "blit_transform" and "merge"');
        $F->blit_write($image1);
        $F->blit_write(
            $F->blit_transform(
                {
                    'blit_data' => $image2,
                    'merge'     => {
                        'dest_blit_data'=> $image1
                    }
                }
            )
        );
        sleep $factor;
    }
}

sub unmask_drawing {
    print_it($F, 'Testing UNMASK Drawing Mode (This is SLOOOW)');
    my $h = int($YY - $F->{'Y_CLIP'});
    my $image1 = $F->load_image(
        {
            'x'      => 0,
            'y'      => $F->{'Y_CLIP'},
            'width'  => ($XX<=320) ? $XX : $XX/2,
            'height' => ($XX<=320) ? $h : $h/2,
            'file'   => "$images_path/RWBY_beuowolf.jpg",
            'center' => 3,
            'convertalpha' => 1
        }
    );
    my $image2 = $F->load_image(
        {
            'x'      => 0,
            'y'      => $F->{'Y_CLIP'},
            'width'  => ($XX<=320) ? $XX : $XX/2,
            'height' => ($XX<=320) ? $h : $h/2,
            'file'   => "$images_path/RWBY_Alternate_Logo.png",
            'center' => 3
        }
    );
    $F->blit_write($image1);
    $F->unmask_mode();
    $F->blit_write($image2);
    $image1 = $F->load_image(
        {
            'x'      => 0,
            'y'      => $F->{'Y_CLIP'},
            'width'  => $XX,
            'height' => $h,
            'file'   => "$images_path/RWBY_beuowolf.jpg",
            'center' => 3,
            'convertalpha' => 1
        }
    );
    $image2 = $F->load_image(
        {
            'x'      => 0,
            'y'      => $F->{'Y_CLIP'},
            'width'  => $XX,
            'height' => $h,
            'file'   => "$images_path/RWBY_Alternate_Logo.png",
            'center' => 3
        }
    );

    if ($F->{'BITS'} > 16) {
        print_it($F,'Simulating UNMASK Mode with the much quicker "blit_transform" and "merge"');
        $F->blit_write($image2);
        $F->blit_write(
            $F->blit_transform(
                {
                    'blit_data' => $image1,
                    'merge'     => {
                        'dest_blit_data'=> $image2
                    }
                }
            )
        );
        sleep $factor;
    }
}

sub print_it {
    my $fb      = shift;
    my $message = shift;
    my $color   = shift || 'FFFF00FF';

    $fb->normal_mode();
    $fb->clip_reset();
    $fb->cls();
    unless ($XX <= 320) {
        $fb->or_mode();

        my $b = $fb->ttf_print(
            {
                'x'            => 5 * $xm,
                'y'            => max(9, 15 * $ym),
                'height'       => max(9, 20 * $ym),
                'color'        => $color,
                'text'         => $message,
                'bounding_box' => 1,
                'center'       => $fb->{'CENTER_X'},
                'antialias'    => 1
            }
        );
        $fb->ttf_print($b);
        $fb->clip_set({'x' => 0, 'y' => $b->{'pheight'} * .75, 'xx' => $XX, 'yy' => $YY});
        $center_y = (($YY - $b->{'pheight'}) / 2) + $b->{'pheight'};
        $fb->normal_mode();
    } else {
        system('clear');
        print STDERR "$message\n";
    }
}

__END__

=head1 NAME

Primitives Demonstration and Testing

=head1 DESCRIPTION

This script demonstrates the capabilities of the Graphics::Framebuffer module

=head1 SYNOPSIS

 perl primitives.pl [file] [device number] [Xemulated resolution] [Yemulated resolution]

=over 2

Examples:

=back

=over 4

 perl primitives.pl file 1 X640 Y480

 perl primitives.pl X1280 Y720

=back

=head1 OPTIONS

=over 2

=item C<file>

Makes the script run in "file handle" mode

=item C<device number> (just the number)

By default, it uses "/dev/fb0", but you can tell it to use any framebuffer device number.  Just the number is needed here.

=item C<X> (followed by the width, no spaces)

This tells the script to tell the Graphics::Framebuffer module to simulate a device of a specific width.  It will center it on the screen.

 "X800" would set the width to 800 pixels.

=item C<Y> (followed by the height, no spaces)

This tells the script to tell the Graphics::Framebuffer module to simulate a device of a specific height.  It will center it on the screen.

 "Y480" would set the height to 480 pixels.

=back

=head1 NOTES

Any benchmarking numbers are written to STDERR.  Animations have benchmarking.

=head1 AUTHOR

Richard Kelsch <rich@rk-internet.com>

=head1 COPYRIGHT

Copyright 2003-2015 Richard Kelsch

This program must always be included as part of the Graphics::Framebuffer package.
