Сохранить GooCanvas2 в файл PNG - PullRequest
1 голос
/ 14 марта 2019

После рисования с помощью GooCanvas2 я пытаюсь сделать «скриншот» холста и сохранить его в файле .PNG.

Этот скрипт предоставляет очень хороший пример использованияGtk2 / GooCanvas, но после преобразования этого скрипта в Gtk3 / GooCanvas2 я получаю ошибку, которую не понимаю:

Write PNG...
*** unhandled exception in callback:
***   `need' is not a valid cairo_status_t value; valid values are: success, no-memory, invalid-restore, invalid-pop-group, no-current-point, invalid-matrix, invalid-status, null-pointer, invalid-string, invalid-path-data, read-error, write-error, surface-finished, surface-type-mismatch, pattern-type-mismatch, invalid-content, invalid-format, invalid-visual, file-not-found, invalid-dash, invalid-dsc-comment, invalid-index, clip-not-representable, temp-file-error, invalid-stride, font-type-mismatch, user-font-immutable, user-font-error, negative-count, invalid-clusters, invalid-slant, invalid-weight at goopng2.pl line 90.
***  ignoring at /usr/share/perl5/Gtk3.pm line 546.

Ошибка генерируется Gtk3 :: Gdk :: PixbufLoader-> write ().Я вообще не модифицировал эту функцию:

$surface->write_to_png_stream (sub {
    my ($closure, $data) = @_;
    $loader->write($data);
});

И это преобразованный скрипт:

#!/usr/bin/perl -w
use strict;

use warnings;
use GooCanvas2;
use Gtk3 '-init';
use Glib qw(TRUE FALSE);

my $window = Gtk3::Window->new('toplevel');
$window->signal_connect('delete_event' => sub { Gtk3->main_quit; });
$window->set_default_size(640, 600);


my $vbox = Gtk3::VBox->new;
$vbox->set_border_width(4);
$vbox->show;
$window->add($vbox);

my $swin = Gtk3::ScrolledWindow->new;
$swin->set_shadow_type('in');
$vbox->pack_start($swin, 1, 1, 0); 

my $canvas = GooCanvas2::Canvas->new();
$canvas->set_size_request(600, 450);
$canvas->set_bounds(0, 0, 1000, 1000);
$swin->add($canvas);

my $root = $canvas->get_root_item();

my $rect = GooCanvas2::CanvasRect->new(
    parent => $root,
    'x' => 100,
    'y' => 100,
    'width' => 400,
    'height' => 400,
    'line-width' => 10,
    'radius-x' => 20,
    'radius-y' => 10,
    'stroke-color' => 'yellow',
    'fill-color' => 'red'
);

my $text = GooCanvas2::CanvasText->new(
    'parent' => $root,
    'text' => "Hello World",
    'x' => 300,
    'y' => 300,
    'width' => -1,
    'anchor' => 'center',
    'font' => 'Sans 24',
);
$text->rotate(45, 300, 300);

# Create PNG                                                                          
my $sb = Gtk3::Button->new_with_label('Write PNG and JPG');                                       
$vbox->pack_start($sb, FALSE, FALSE, 0);                                               
$sb->show;                                                                             
$sb->signal_connect("clicked", \&write_png_clicked, $canvas);                          

$window->show_all();
Gtk3->main;

sub write_png_clicked {
    my ($but, $canvas) = @_;
    print "Write PNG...\n";

    my $surface = Cairo::ImageSurface->create ('rgb24', 1000, 1000);
    # also argb32 is available
    # my $surface = Cairo::ImageSurface->create ('argb32', 1000, 1000);

    my $cr = Cairo::Context->create($surface);

    # make a background rectangle filled white so saved file looks same as screen
    # otherwise a black background may appear, it's like pdf, if it isn't
    # drawn , it will be a black background, It won't automagically pick up
    # a white background on a canvas
    $cr->rectangle( 0, 0, 1000, 1000 );
    $cr->set_source_rgb( 1, 1, 1 );
    $cr->fill;

    $canvas->render($cr, undef, 1);

    # this works, but see below for way to use pixbuf and jpg
    #    my $status = $surface->write_to_png ("$0.png");
    #    print "$status\n";

    my $loader = Gtk3::Gdk::PixbufLoader->new;
        $surface->write_to_png_stream (sub {
        my ($closure, $data) = @_;
        $loader->write($data);
    });
    $loader->close;
    my $pixbuf = $loader->get_pixbuf;

    print $pixbuf->get_bits_per_sample(),"\n";
    print $pixbuf->get_colorspace(),"\n";

    $pixbuf->save ("$0.png", 'png');
    print "done png\n";
    $pixbuf->save ("$0.jpg", 'jpeg', quality => 100); 
    print "done jpg\n";

    return TRUE;
}

1 Ответ

0 голосов
/ 15 марта 2019

* необработанное исключение в обратном вызове: * «потребность» не является допустимым значением cairo_status_t; допустимые значения: success, no-memory, [...] в строке 90 goopng2.pl. *** игнорируя на /usr/share/perl5/Gtk3.pm строка 546.

Запустив отладчик для вашего кода, я увидел, что $loader->write($data) вызвал исключение:

need an array ref to convert to GArray

и write_to_png_stream() не ожидали такого типа исключения и обрезали сообщение до первого слова "need", как видно из сообщения об ошибке Glib вверху: `need' is not a valid cairo_status_t value ...

Путем проб и ошибок я обнаружил, что могу передать аргумент $buffer как массив символов, а не как строку perl:

sub write_png_clicked {
    my ($but, $canvas) = @_;
    print "Write PNG...\n";

    my $surface = Cairo::ImageSurface->create ('rgb24', 1000, 1000);
    my $cr = Cairo::Context->create($surface);
    $cr->rectangle( 0, 0, 1000, 1000 );
    $cr->set_source_rgb( 1, 1, 1 );
    $cr->fill;
    $canvas->render($cr, undef, 1);
    my $loader = Gtk3::Gdk::PixbufLoader->new;
    $surface->write_to_png_stream (
        sub {
            my ($loader, $buffer) = @_;
            $loader->write([map ord, split //, $buffer]);
            return TRUE;
        }, $loader
    );
    $loader->close;
    my $pixbuf = $loader->get_pixbuf;

    print $pixbuf->get_bits_per_sample(),"\n";
    print $pixbuf->get_colorspace(),"\n";

    $pixbuf->save ("test.png", 'png');
    print "done png\n";
    $pixbuf->save ("test.jpg", 'jpeg', quality => 100); 
    print "done jpg\n";
    return TRUE;
}

Редактировать

Чтобы сохранить только часть холста, вы можете передать параметр GooCanvasBounds в метод render():

my $bounds = GooCanvas2::CanvasBounds->new();
$bounds->x1(50);
$bounds->x2(250);
$bounds->y1(50);
$bounds->y2(250);
$canvas->render($cr, $bounds, 1);

Редактировать 2 :

Для захвата области в определенной позиции и определенной ширине и высоте:

my $img_width = 200;
my $img_height = 200;
my $img_x0 = 100;
my $img_y0 = 100;
my $surface = Cairo::ImageSurface->create ('rgb24', $img_width, $img_height);
$cr->translate(-$img_x0,-$img_y0);
$canvas->render($cr, undef, 1);
...