Gtk3-ImageView-12/0000755000175000017500000000000014702603320014202 5ustar somebodysomebodyGtk3-ImageView-12/README0000644000175000017500000001346414702603320015072 0ustar somebodysomebodyNAME Gtk3::ImageView - Image viewer widget for Gtk3 VERSION 9 SYNOPSIS use Gtk3::ImageView; Gtk3->init; $window = Gtk3::Window->new(); $view = Gtk3::ImageView->new; $view->set_pixbuf($pixbuf, TRUE); $window->add($view); $window->show_all; DESCRIPTION The Gtk3::ImageView widget allows the user to zoom, pan and select the specified image and provides hooks to allow additional tools, e.g. painter, to be created and used. Gtk3::ImageView is a Gtk3 port of Gtk2::ImageView. To discuss Gtk3::ImageView or gtk3-perl, ask questions and flame/praise the authors, join gtk-perl-list@gnome.org at lists.gnome.org. SUBROUTINES/METHODS $view->set_pixbuf( $pixbuf, $zoom_to_fit ) Defines the image to view. The optional zoom_to_fit parameter specifies whether to zoom to fit the image or not. $view->get_pixbuf Returns the image currently being viewed. $view->get_pixbuf_size Returns a hash of containing the size of the current image in width and height keys. $view->set_zoom($zoom) Specifies the zoom level. $view->get_zoom Returns the current zoom level. $view->set_zoom_to_fit($zoom_to_fit, $limit) Specifies whether to zoom to fit or not. If limit is defined, such automatic zoom will not zoom more than it. If the limit is undefined, the previously set limit is used, initially it's unlimited. $view->zoom_to_box( $box, $additional_factor ) Specifies a box to zoom to, including an additional zoom factor $view->zoom_to_selection($context_factor) Zooms to the current selection, plus an addition zoom factor. Shortcut for $view->zoom_to_box( $view->get_selection, $context_factor ); $view->get_zoom_to_fit Returns whether the view is currently zoom to fit or not. $view->zoom_in Increases the current zoom by "zoom-step" times (defaults to 2). $view->zoom_out Decreases the current zoom by "zoom-step" times (defaults to 2). $view->zoom_to_fit Shortcut for $view->set_zoom_to_fit(TRUE); $view->set_fitting( $value ) Shortcut for $view->set_zoom_to_fit($value, 1); which means that it won't stretch a small image to a large window. Provided (despite the ambiguous name) for compatibility with Gtk2::ImageView. $view->set_offset( $x, $y ) Set the current view offset (i.e. pan position). $view->set_offset Returns the current view offset (i.e. pan position). $view->get_viewport Returns a hash containing the position and size of the current viewport. $view->set_tool Set the current tool (i.e. mode) - an object of a subclass of "Gtk3::ImageView::Tool". Here are some known subclasses of it: * "Gtk3::ImageView::Tool::Dragger" allows the image to be dragged when zoomed. * "Gtk3::ImageView::Tool::Selector" allows the user to select a rectangular area of the image. * "Gtk3::ImageView::Tool::SelectorDragger" selects or drags with different mouse buttons. Don't rely too much on how Tool currently interacts with ImageView. $view->get_tool Returns the current tool (i.e. mode). $view->set_selection($selection) Set the current selection as a hash of position and size. $view->get_selection Returns the current selection as a hash of position and size. $view->set_resolution_ratio($ratio) Set the ratio of the resolutions in the x and y directions, allowing images with non-square pixels to be correctly displayed. $view->get_resolution_ratio Returns the current resolution ratio. $view->set_interpolation The interpolation method to use, from "cairo_filter_t" type. Possible values are lowercase strings like "nearest". To use different interpolation depending on zoom, set it in the "zoom-changed" signal. $view->get_interpolation Returns the current interpolation method. DIAGNOSTICS CONFIGURATION AND ENVIRONMENT DEPENDENCIES INCOMPATIBILITIES Porting from Gtk2::ImageView * set_from_pixbuf() was renamed to set_pixbuf() and now its second argument means zoom_to_fit() instead of set_fitting(). * set_fitting(TRUE) used to be the default, now you need to call it explicitly if you want that behavior. However, once it's called, new calls to set_from_pixbuf() won't reset it, see set_zoom_to_fit() for more details.. * Drag and drop now can be triggered by subscribing to "dnd-start" signal, and calling "$view->drag_begin_with_coordinates()" from the handler. drag_source_set() won't work. * "Gtk2::ImageView::ScrollWin" replacement is not yet implemented. * set_transp() is now available through CSS instead, e.g. via .imageview.transparent { background-image: url('checkers.svg'); } * set_interpolation() now accepts "cairo_filter_t" instead of "GdkInterpType" . BUGS AND LIMITATIONS This should be rewritten on C, and Perl bindings should be provided via Glib Object Introspection. AUTHOR Jeffrey Ratcliffe, Alexey Sokolov LICENSE AND COPYRIGHT Copyright (C) 2018--2020 by Jeffrey Ratcliffe Copyright (C) 2020 by Google LLC Modelled after the GtkImageView C widget by Björn Lindqvist This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.5 or, at your option, any later version of Perl 5 you may have available. Gtk3-ImageView-12/Changes0000644000175000017500000000055414702603320015501 0ustar somebodysomebody12 2024-10-13 00:31:26 IST - Removed perl feature switch, for compatibility with newer perl - Made linter on CI happy 11 2024-10-01 17:08:47 IST - Replaced deprecated given/when with if/elsif/else - Fixed Dist::Zilla config, hopefully 10 2021-10-03 11:33:07 IST - Switched to Dist::Zilla - Made the selector tool to be draggable Gtk3-ImageView-12/LICENSE0000644000175000017500000004660614702603320015223 0ustar somebodysomebodyThis software is copyright (c) 2018-2024 by Jeffrey Ratcliffe, Alexey Sokolov and Google. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2018-2024 by Jeffrey Ratcliffe, Alexey Sokolov and Google. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Perl Artistic License 1.0 --- This software is Copyright (c) 2018-2024 by Jeffrey Ratcliffe, Alexey Sokolov and Google. This is free software, licensed under: The Perl Artistic License 1.0 The "Artistic License" Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package. 7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language. 8. Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package. 9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End Gtk3-ImageView-12/dist.ini0000644000175000017500000000106414702603320015647 0ustar somebodysomebodyname = Gtk3-ImageView author = Jeffrey Ratcliffe author = Alexey Sokolov license = Perl_5 copyright_year = 2018-2024 copyright_holder = Jeffrey Ratcliffe, Alexey Sokolov and Google [@Starter::Git] revision = 5 installer = MakeMaker::Awesome -remove = RunExtraTests -remove = TestRelease managed_versions = 1 Git::GatherDir.exclude_filename = Makefile.PL Git::Tag.tag_format = v%v Git::Tag.tag_message = v%v [Prereqs / BuildRequires] Glib = 1.210 [AutoPrereqs] [GithubMeta] issues = 1 [PerlTidy] [CopyFilesFromBuild] copy = Makefile.PL Gtk3-ImageView-12/META.yml0000644000175000017500000000330414702603320015453 0ustar somebodysomebody--- abstract: 'Image viewer widget for Gtk3' author: - 'Jeffrey Ratcliffe ' - 'Alexey Sokolov' build_requires: Carp::Always: '0' ExtUtils::MakeMaker: '0' File::Spec: '0' File::Temp: '0' Glib: '1.210' Image::Magick: '0' MIME::Base64: '0' Test::Deep: '0' Test::MockObject: '0' Test::More: '0' Try::Tiny: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.032, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Gtk3-ImageView no_index: directory: - eg - examples - inc - share - t - xt provides: Gtk3::ImageView: file: lib/Gtk3/ImageView.pm version: '12' Gtk3::ImageView::Tool: file: lib/Gtk3/ImageView/Tool.pm version: '12' Gtk3::ImageView::Tool::Dragger: file: lib/Gtk3/ImageView/Tool/Dragger.pm version: '12' Gtk3::ImageView::Tool::Selector: file: lib/Gtk3/ImageView/Tool/Selector.pm version: '12' Gtk3::ImageView::Tool::SelectorDragger: file: lib/Gtk3/ImageView/Tool/SelectorDragger.pm version: '12' requires: Cairo: '0' Carp: '0' Glib: '1.210' Glib::Object::Subclass: '0' Gtk3: '0' List::Util: '0' Readonly: '0' Scalar::Util: '0' base: '0' if: '0' strict: '0' warnings: '0' resources: bugtracker: https://github.com/DarthGandalf/gtk3-imageview/issues homepage: https://github.com/DarthGandalf/gtk3-imageview repository: https://github.com/DarthGandalf/gtk3-imageview.git version: '12' x_generated_by_perl: v5.40.0 x_serialization_backend: 'YAML::Tiny version 1.74' x_spdx_expression: 'Artistic-1.0-Perl OR GPL-1.0-or-later' Gtk3-ImageView-12/MANIFEST0000644000175000017500000000106314702603320015333 0ustar somebodysomebody# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.032. Changes LICENSE MANIFEST META.json META.yml Makefile.PL README README.md dist.ini lib/Gtk3/ImageView.pm lib/Gtk3/ImageView/Tool.pm lib/Gtk3/ImageView/Tool/Dragger.pm lib/Gtk3/ImageView/Tool/Selector.pm lib/Gtk3/ImageView/Tool/SelectorDragger.pm t/00-report-prereqs.dd t/00-report-prereqs.t t/1_basics.t t/2_transparent.t t/2color.svg t/3_zoom.t t/4_select.t t/5_filter.t t/bigpic.svg t/perlcriticrc t/transp-blue.svg t/transp-green.svg xt/author/00-compile.t xt/author/pod-syntax.t Gtk3-ImageView-12/README.md0000644000175000017500000000434714702603320015471 0ustar somebodysomebody# Gtk3::ImageView Gtk3 imager viewer widget modelled after the GtkImageView C widget by Björn Lindqvist To discuss Gtk3::ImageView or gtk3-perl, ask questions and flame/praise the authors, join [mailing list](mailto:gtk-perl-list@gnome.org) at lists.gnome.org. ## INSTALLATION This module is available from [CPAN](https://metacpan.org/pod/Gtk3::ImageView). To install it from source instead install [Dist::Zilla](https://metacpan.org/pod/Dist::Zilla), then type the following: ```shell dzil install ``` To avoid installing to a system directory, you can change the installation prefix at Makefile.PL time with ```shell dzil install --install-command='cpanm -l /tmp/foo .' ``` Or you can use `dzil build` to get the directory of the release in current dir, cd there, and build/install it with via generated Makefile.PL manually. `dzil help`, [dzil.org](http://dzil.org) and [Dist::Zilla::Tutorial](https://metacpan.org/pod/Dist::Zilla::Tutorial#BUILDING-YOUR-DIST) have more details. This will install the module to the subdirectory lib/perl5 under the given prefix. If this is not already in perl's include path, you'll need to tell perl how to get to this library directory so you can use it; there are three ways: - in your environment (the easiest): ```shell PERL5LIB=/some/other/place/lib/perl5/site_perl export PERL5LIB ``` - on the perl command line: ```shell perl -I /some/other/place/lib/perl5/site_perl yourscript ``` - in the code of your perl script: ```shell use lib '/some/other/place/lib/perl5/site_perl'; ``` ## DEPENDENCIES This module requires these other modules and libraries: - perl >= 5.8.0 - Glib >= 1.163 (Perl module) - GTK+ 3.x (C library) - Gtk3 (Perl module) - Readonly (Perl module) ## BUG REPORTS Please submit bug reports [here](https://github.com/carygravel/gtk3-imageview/issues) ## COPYRIGHT AND LICENSE Copyright (C) 2018--2020 by Jeffrey Ratcliffe Copyright (C) 2020 Google LLC, contributed by Alexey Sokolov Modelled after the GtkImageView C widget by Björn Lindqvist This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.5 or, at your option, any later version of Perl 5 you may have available. Gtk3-ImageView-12/META.json0000644000175000017500000000623714702603320015633 0ustar somebodysomebody{ "abstract" : "Image viewer widget for Gtk3", "author" : [ "Jeffrey Ratcliffe ", "Alexey Sokolov" ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.032, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Gtk3-ImageView", "no_index" : { "directory" : [ "eg", "examples", "inc", "share", "t", "xt" ] }, "prereqs" : { "build" : { "requires" : { "Glib" : "1.210" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "develop" : { "requires" : { "File::Spec" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Test::More" : "0", "Test::Pod" : "1.41", "perl" : "5.006" } }, "runtime" : { "requires" : { "Cairo" : "0", "Carp" : "0", "Glib" : "1.210", "Glib::Object::Subclass" : "0", "Gtk3" : "0", "List::Util" : "0", "Readonly" : "0", "Scalar::Util" : "0", "base" : "0", "if" : "0", "strict" : "0", "warnings" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "Carp::Always" : "0", "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "File::Temp" : "0", "Image::Magick" : "0", "MIME::Base64" : "0", "Test::Deep" : "0", "Test::MockObject" : "0", "Test::More" : "0", "Try::Tiny" : "0" } } }, "provides" : { "Gtk3::ImageView" : { "file" : "lib/Gtk3/ImageView.pm", "version" : "12" }, "Gtk3::ImageView::Tool" : { "file" : "lib/Gtk3/ImageView/Tool.pm", "version" : "12" }, "Gtk3::ImageView::Tool::Dragger" : { "file" : "lib/Gtk3/ImageView/Tool/Dragger.pm", "version" : "12" }, "Gtk3::ImageView::Tool::Selector" : { "file" : "lib/Gtk3/ImageView/Tool/Selector.pm", "version" : "12" }, "Gtk3::ImageView::Tool::SelectorDragger" : { "file" : "lib/Gtk3/ImageView/Tool/SelectorDragger.pm", "version" : "12" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/DarthGandalf/gtk3-imageview/issues" }, "homepage" : "https://github.com/DarthGandalf/gtk3-imageview", "repository" : { "type" : "git", "url" : "https://github.com/DarthGandalf/gtk3-imageview.git", "web" : "https://github.com/DarthGandalf/gtk3-imageview" } }, "version" : "12", "x_generated_by_perl" : "v5.40.0", "x_serialization_backend" : "JSON::XS version 4.03", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later" } Gtk3-ImageView-12/t/0000755000175000017500000000000014702603320014445 5ustar somebodysomebodyGtk3-ImageView-12/t/3_zoom.t0000644000175000017500000000233414702603320016042 0ustar somebodysomebodyuse warnings; use strict; use Test::More tests => 7; use Test::Deep; BEGIN { use Glib qw/TRUE FALSE/; use Gtk3 -init; use_ok('Gtk3::ImageView'); } my $window = Gtk3::Window->new('toplevel'); $window->set_size_request( 300, 200 ); my $view = Gtk3::ImageView->new; my $scale = $view->get('scale-factor'); $window->add($view); $window->show_all; $view->set_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file('t/bigpic.svg'), TRUE ); cmp_deeply( $view->get_zoom, num( 0.2 * $scale, 0.0001 ), 'shrinked' ); $view->set_zoom(1); # the transp-green picture is 100x100 which is less than 200. $view->set_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file('t/transp-green.svg'), FALSE ); is( $view->get_zoom, 1, 'picture fully visible' ); $view->set_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file('t/transp-green.svg'), TRUE ); is( $view->get_zoom, 2 * $scale, 'zoomed' ); $view->set_fitting(TRUE); is( $view->get_zoom, $scale, 'no need to zoom' ); $view->set_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file('t/transp-green.svg'), TRUE ); is( $view->get_zoom, $scale, 'no need to zoom even when TRUE' ); $view->set_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file('t/bigpic.svg'), TRUE ); cmp_deeply( $view->get_zoom, num( 0.2 * $scale, 0.0001 ), 'still shrinked' ); Gtk3-ImageView-12/t/1_basics.t0000644000175000017500000001447514702603320016331 0ustar somebodysomebodyuse warnings; use strict; use Try::Tiny; use File::Temp; use Image::Magick; use Test::Deep; use Test::More tests => 38; BEGIN { use Glib qw/TRUE FALSE/; use Gtk3 -init; use_ok('Gtk3::ImageView'); } ######################### my $view = Gtk3::ImageView->new; isa_ok( $view, 'Gtk3::ImageView' ); isa_ok( $view->get_tool, 'Gtk3::ImageView::Tool::Dragger', 'get_tool() defaults to dragger' ); my $tmp = File::Temp->new( SUFFIX => '.png' ); my $image = Image::Magick->new(); my $x = $image->Read('rose:'); $x = $image->Write( filename => $tmp ); warn "$x" if "$x"; my $signal; $signal = $view->signal_connect( 'offset-changed' => sub { my ( $widget, $x, $y ) = @_; $view->signal_handler_disconnect($signal); SKIP: { skip "I don't know how offset is supposed to work with HiDPI", 2 if $view->get('scale-factor') > 1; is $x, 0, 'emitted offset-changed signal x'; is $y, 11, 'emitted offset-changed signal y'; } } ); $view->set_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file($tmp), TRUE ); SKIP: { skip "I don't know how offset is supposed to work with HiDPI", 1 if $view->get('scale-factor') > 1; cmp_deeply( $view->get_viewport, { x => 0, y => num( -12, 0.001 ), width => num( 70, 0.001 ), height => num( 70, 0.001 ), }, 'get_viewport' ); } SKIP: { skip 'not yet', 2; isa_ok( $view->get_draw_rect, 'Gtk3::Gdk::Rectangle' ); ok( $view->get_check_colors, 'get_check_colors()' ); } isa_ok( $view->get_pixbuf, 'Gtk3::Gdk::Pixbuf', 'get_pixbuf()' ); is_deeply( $view->get_pixbuf_size, { width => 70, height => 46 }, 'get_pixbuf_size' ); is_deeply( $view->get_allocation, { x => -1, y => -1, width => 1, height => 1 }, 'get_allocation' ); cmp_deeply( $view->get_zoom, num( 0.01428 * $view->get('scale-factor'), 0.001 ), 'get_zoom()' ); $signal = $view->signal_connect( 'zoom-changed' => sub { my ( $widget, $zoom ) = @_; $view->signal_handler_disconnect($signal); is $zoom, 1, 'emitted zoom-changed signal'; } ); $view->set_zoom(1); $signal = $view->signal_connect( 'selection-changed' => sub { my ( $widget, $selection ) = @_; $view->signal_handler_disconnect($signal); is_deeply( $selection, { x => 10, y => 10, width => 10, height => 10 }, 'emitted selection-changed signal' ); } ); $view->set_selection( { x => 10, y => 10, width => 10, height => 10 } ); is_deeply( $view->get_selection, { x => 10, y => 10, width => 10, height => 10 }, 'get_selection' ); $signal = $view->signal_connect( 'tool-changed' => sub { my ( $widget, $tool ) = @_; $view->signal_handler_disconnect($signal); isa_ok( $tool, 'Gtk3::ImageView::Tool::Selector', 'emitted tool-changed signal' ); } ); $view->set_tool('selector'); $view->set_selection( { x => -10, y => -10, width => 20, height => 20 } ); is_deeply( $view->get_selection, { x => 0, y => 0, width => 10, height => 10 }, 'selection cannot overlap top left border' ); $view->set_selection( { x => 10, y => 10, width => 80, height => 50 } ); is_deeply( $view->get_selection, { x => 10, y => 10, width => 60, height => 36 }, 'selection cannot overlap bottom right border' ); $view->set_resolution_ratio(2); is $view->get_resolution_ratio, 2, 'get/set_resolution_ratio()'; SKIP: { skip 'not yet', 5; ok( Gtk3::ImageView::Zoom->get_min_zoom < Gtk3::ImageView::Zoom->get_max_zoom, 'Ensure that the gtkimageview.zooms_* functions are present and work as expected.' ); is( defined $view->get_black_bg, TRUE, 'get_black_bg()' ); is( defined $view->get_show_frame, TRUE, 'get_show_frame()' ); is( defined $view->get_interpolation, TRUE, 'get_interpolation()' ); is( defined $view->get_show_cursor, TRUE, 'get_show_cursor()' ); } try { $view->set_pixbuf( 'Hi mom!', TRUE ) } catch { like( $_, qr/type/, 'A TypeError is raised when set_pixbuf() is called with something that is not a pixbuf.' ) }; $view->set_pixbuf( undef, TRUE ); is( defined $view->get_pixbuf, FALSE, 'correctly cleared pixbuf' ); is_deeply( $view->get_viewport, { x => 0, y => 0, width => 1, height => 1 }, 'correctly cleared viewport' ); try { $view->set_pixbuf( undef, FALSE ); pass 'correctly cleared pixbuf2' } catch { fail 'correctly cleared pixbuf2' }; SKIP: { skip 'not yet', 10; ok( !$view->get_draw_rect, 'correctly cleared draw rectangle' ); $view->size_allocate( Gtk3::Gdk::Rectangle->new( 0, 0, 100, 100 ) ); $view->set_pixbuf( Gtk3::Gdk::Pixbuf->new( Gtk3::Gdk::colormap_get_system(), FALSE, 8, 50, 50 ) ); my $rect = $view->get_viewport; ok( ( $rect->x == 0 and $rect->y == 0 and $rect->width == 50 and $rect->height == 50 ), 'Ensure that getting the viewport of the view works as expected.' ); can_ok( $view, qw(get_check_colors) ); $rect = $view->get_draw_rect; ok( ( $rect->x == 25 and $rect->y == 25 and $rect->width == 50 and $rect->height == 50 ), 'Ensure that getting the draw rectangle works as expected.' ); $view->set_pixbuf( Gtk3::Gdk::Pixbuf->new( Gtk3::Gdk::colormap_get_system(), FALSE, 8, 200, 200 ) ); $view->set_zoom(1); $view->set_offset( 0, 0 ); $rect = $view->get_viewport; ok( ( $rect->x == 0 and $rect->y == 0 ), 'Ensure that setting the offset works as expected.' ); $view->set_offset( 100, 100, TRUE ); $rect = $view->get_viewport; ok( ( $rect->x == 100 and $rect->y == 100 ), 'Ensure that setting the offset works as expected.' ); $view->set_transp( 'color', 0xff0000 ); my ( $col1, $col2 ) = $view->get_check_colors; ok( ( $col1 == 0xff0000 and $col2 == 0xff0000 ), 'Ensure that setting the views transparency settings works as expected.' ); $view->set_transp('grid'); ok( defined Glib::Type->list_values('Gtk3::ImageView::Transp'), 'Check GtkImageTransp enum.' ); } Gtk3-ImageView-12/t/2color.svg0000644000175000017500000000027414702603320016371 0ustar somebodysomebody Gtk3-ImageView-12/t/4_select.t0000644000175000017500000000225314702603320016336 0ustar somebodysomebodyuse warnings; use strict; use Try::Tiny; use File::Temp; use Test::More tests => 2; use Test::MockObject; use Test::Deep; use Carp::Always; BEGIN { use Glib qw/TRUE FALSE/; use Gtk3 -init; use_ok('Gtk3::ImageView'); } ######################### my $window = Gtk3::Window->new('toplevel'); $window->set_size_request( 300, 200 ); my $view = Gtk3::ImageView->new; $window->add($view); $view->set_tool('selector'); $view->set_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file('t/transp-green.svg'), TRUE ); $window->show_all; $window->hide; $view->set_zoom(8); my $event = Test::MockObject->new; $event->set_always( 'button', 0 ); $event->set_always( 'x', 7 ); $event->set_always( 'y', 5 ); $view->get_tool->button_pressed($event); $event->set_always( 'x', 93 ); $event->set_always( 'y', 67 ); $view->get_tool->button_released($event); SKIP: { skip "I can't figure out the correct formula here which works with HiDPI", 1 if $view->get('scale-factor') > 1; cmp_deeply( $view->get_selection, { x => num(32), y => num(38), width => 11, height => 8 }, 'get_selection' ); } Gtk3-ImageView-12/t/5_filter.t0000644000175000017500000000507514702603320016352 0ustar somebodysomebodyuse warnings; use strict; use File::Temp; use Image::Magick; use Test::More tests => 10; use MIME::Base64; BEGIN { use Glib qw/TRUE FALSE/; use Gtk3 -init; use_ok('Gtk3::ImageView'); Glib::Object::Introspection->setup( basename => 'GdkX11', version => '3.0', package => 'Gtk3::GdkX11', ); } my $window = Gtk3::Window->new('toplevel'); $window->set_size_request( 300, 200 ); my $view = Gtk3::ImageView->new; $view->set_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file('t/2color.svg'), TRUE ); $window->add($view); $window->show_all; my $xid = $window->get_window->get_xid; $view->set_zoom(15); $view->set_interpolation('bilinear'); my $image = Image::Magick->new( magick => 'png' ); Glib::Timeout->add( 1000, sub { $image->Read("x:$xid"); Gtk3::main_quit; return FALSE; } ); Gtk3::main; diag('PNG of the blurred window:'); diag( encode_base64( $image->ImageToBlob ) ); my $x = $image->Get('width') / 2; my $y = $image->Get('height') / 2; my @middle = $image->GetPixel( x => $x, y => $y ); is_deeply( \@middle, [ 1, 0, 0 ], 'middle pixel should be red' ); my $found; my @pixel; $found = 0; while ( $x > 0 ) { @pixel = $image->GetPixel( x => $x, y => $y ); if ( join( ',', @pixel ) ne '1,0,0' ) { $found = 1; last; } $x--; } is( $found, 1, 'there is non-red outside' ); my $blurred_x = $x; $found = 0; while ( $x > 0 ) { @pixel = $image->GetPixel( x => $x, y => $y ); if ( join( ',', @pixel ) eq '0,0,1' ) { $found = 1; last; } $x--; } is( $found, 1, 'there is blue outside' ); my $fullblue_x = $x; cmp_ok( $fullblue_x, '<', $blurred_x ); $view->set_interpolation('nearest'); $image = Image::Magick->new( magick => 'png' ); Glib::Timeout->add( 1000, sub { $image->Read("x:$xid"); Gtk3::main_quit; return FALSE; } ); Gtk3::main; diag('PNG of the crisp window:'); diag( encode_base64( $image->ImageToBlob ) ); @pixel = $image->GetPixel( x => $fullblue_x, y => $y ); is_deeply( \@pixel, [ 0, 0, 1 ], 'blue pixel should still be blue' ); $found = 0; while ( $x <= $blurred_x ) { @pixel = $image->GetPixel( x => $x, y => $y ); if ( join( ',', @pixel ) ne '0,0,1' ) { $found = 1; last; } $x++; } is( $found, 1, 'there is non-blue inside' ); is_deeply( \@pixel, [ 1, 0, 0 ], 'red pixel should be immediatelly near blue one' ); cmp_ok( $fullblue_x, '<', $x, 'sharp edge should be within blurred edge (1)' ); cmp_ok( $x, '<', $blurred_x, 'sharp edge should be within blurred edge (2)' ); Gtk3-ImageView-12/t/bigpic.svg0000644000175000017500000000011014702603320016413 0ustar somebodysomebody Gtk3-ImageView-12/t/perlcriticrc0000644000175000017500000000074714702603320017065 0ustar somebodysomebodyseverity = 1 [RegularExpressions::RequireExtendedFormatting] minimum_regex_length_to_complain_about = 3 [ValuesAndExpressions::ProhibitInterpolationOfLiterals] allow_if_string_contains_single_quote = 1 [ValuesAndExpressions::ProhibitMagicNumbers] allowed_values = 0 1 2 0.5 [Documentation::PodSpelling] stop_words = Alexey behavior Björn CSS dragger gtk Gtk GtkImageView ImageView Lindqvist LLC perl Ratcliffe Sokolov subclasses viewport [-CodeLayout::ProhibitParensWithBuiltins] Gtk3-ImageView-12/t/2_transparent.t0000644000175000017500000000412314702603320017414 0ustar somebodysomebodyuse warnings; use strict; use File::Temp; use Image::Magick; use Test::More tests => 4; use MIME::Base64; BEGIN { use Glib qw/TRUE FALSE/; use Gtk3 -init; use_ok('Gtk3::ImageView'); Glib::Object::Introspection->setup( basename => 'GdkX11', version => '3.0', package => 'Gtk3::GdkX11', ); } my $window = Gtk3::Window->new('toplevel'); $window->set_size_request( 300, 200 ); my $css_provider_alpha = Gtk3::CssProvider->new; Gtk3::StyleContext::add_provider_for_screen( $window->get_screen, $css_provider_alpha, 0 ); $css_provider_alpha->load_from_data( " .imageview.transparent { background-color: #ff0000; background-image: none; } .imageview { background-image: url('t/transp-blue.svg'); } " ); my $view = Gtk3::ImageView->new; $view->set_pixbuf( Gtk3::Gdk::Pixbuf->new_from_file('t/transp-green.svg'), TRUE ); $window->add($view); $window->show_all; my $xid = $window->get_window->get_xid; my $image = Image::Magick->new( magick => 'png' ); Glib::Timeout->add( 1000, sub { $image->Read("x:$xid"); Gtk3::main_quit; return FALSE; } ); Gtk3::main; diag('PNG of the shown window:'); diag( encode_base64( $image->ImageToBlob ) ); my $x = $image->Get('width') / 2; my $y = $image->Get('height') / 2; my @middle = $image->GetPixel( x => $x, y => $y ); is_deeply( \@middle, [ 0, 1, 0 ], 'middle pixel should be green' ); my $found; $found = 0; while ( $x > 0 ) { my @pixel = $image->GetPixel( x => $x, y => $y ); if ( join( ',', @pixel ) eq '1,0,0' ) { $found = 1; last; } $x--; } is( $found, 1, 'there is red background' ); $found = 0; while ( $x > 0 ) { my @pixel = $image->GetPixel( x => $x, y => $y ); # the blue chessboard can become blurred with hidpi :( my @pixel_below = $image->GetPixel( x => $x, y => $y + $view->get('scale-factor') ); if ( join( ',', @pixel ) eq '0,0,1' or join( ',', @pixel_below ) eq '0,0,1' ) { $found = 1; last; } $x--; } is( $found, 1, 'there is blue outside' ); Gtk3-ImageView-12/t/transp-blue.svg0000644000175000017500000000027614702603320017427 0ustar somebodysomebody Gtk3-ImageView-12/t/transp-green.svg0000644000175000017500000000020414702603320017567 0ustar somebodysomebody Gtk3-ImageView-12/t/00-report-prereqs.t0000644000175000017500000001432214702603320020043 0ustar somebodysomebody#!perl use strict; use warnings; # This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.029 use Test::More tests => 1; use ExtUtils::MakeMaker; use File::Spec; # from $version::LAX my $lax_version_re = qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )? | (?:\.[0-9]+) (?:_[0-9]+)? ) | (?: v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )? | (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)? ) )/x; # hide optional CPAN::Meta modules from prereq scanner # and check if they are available my $cpan_meta = "CPAN::Meta"; my $cpan_meta_pre = "CPAN::Meta::Prereqs"; my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic # Verify requirements? my $DO_VERIFY_PREREQS = 1; sub _max { my $max = shift; $max = ( $_ > $max ) ? $_ : $max for @_; return $max; } sub _merge_prereqs { my ( $collector, $prereqs ) = @_; # CPAN::Meta::Prereqs object if ( ref $collector eq $cpan_meta_pre ) { return $collector->with_merged_prereqs( CPAN::Meta::Prereqs->new($prereqs) ); } # Raw hashrefs for my $phase ( keys %$prereqs ) { for my $type ( keys %{ $prereqs->{$phase} } ) { for my $module ( keys %{ $prereqs->{$phase}{$type} } ) { $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module}; } } } return $collector; } my @include = qw( ); my @exclude = qw( ); # Add static prereqs to the included modules list my $static_prereqs = do './t/00-report-prereqs.dd'; # Merge all prereqs (either with ::Prereqs or a hashref) my $full_prereqs = _merge_prereqs( ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ), $static_prereqs ); # Add dynamic prereqs to the included modules list (if we can) my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; my $cpan_meta_error; if ( $source && $HAS_CPAN_META && ( my $meta = eval { CPAN::Meta->load_file($source) } ) ) { $full_prereqs = _merge_prereqs( $full_prereqs, $meta->prereqs ); } else { $cpan_meta_error = $@; # capture error from CPAN::Meta->load_file($source) $source = 'static metadata'; } my @full_reports; my @dep_errors; my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; # Add static includes into a fake section for my $mod (@include) { $req_hash->{other}{modules}{$mod} = 0; } for my $phase (qw(configure build test runtime develop other)) { next unless $req_hash->{$phase}; next if ( $phase eq 'develop' and not $ENV{AUTHOR_TESTING} ); for my $type (qw(requires recommends suggests conflicts modules)) { next unless $req_hash->{$phase}{$type}; my $title = ucfirst($phase) . ' ' . ucfirst($type); my @reports = [qw/Module Want Have/]; for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { next if grep { $_ eq $mod } @exclude; my $want = $req_hash->{$phase}{$type}{$mod}; $want = "undef" unless defined $want; $want = "any" if !$want && $want == 0; if ( $mod eq 'perl' ) { push @reports, [ 'perl', $want, $] ]; next; } my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e File::Spec->catfile( $_, $file ) } @INC; if ($prefix) { my $have = MM->parse_version( File::Spec->catfile( $prefix, $file ) ); $have = "undef" unless defined $have; push @reports, [ $mod, $want, $have ]; if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { if ( $have !~ /\A$lax_version_re\z/ ) { push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; } elsif ( !$full_prereqs->requirements_for( $phase, $type ) ->accepts_module( $mod => $have ) ) { push @dep_errors, "$mod version '$have' is not in required range '$want'"; } } } else { push @reports, [ $mod, $want, "missing" ]; if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { push @dep_errors, "$mod is not installed ($req_string)"; } } } if (@reports) { push @full_reports, "=== $title ===\n\n"; my $ml = _max( map { length $_->[0] } @reports ); my $wl = _max( map { length $_->[1] } @reports ); my $hl = _max( map { length $_->[2] } @reports ); if ( $type eq 'modules' ) { splice @reports, 1, 0, [ "-" x $ml, "", "-" x $hl ]; push @full_reports, map { sprintf( " %*s %*s\n", -$ml, $_->[0], $hl, $_->[2] ) } @reports; } else { splice @reports, 1, 0, [ "-" x $ml, "-" x $wl, "-" x $hl ]; push @full_reports, map { sprintf( " %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2] ) } @reports; } push @full_reports, "\n"; } } } if (@full_reports) { diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; } if ( $cpan_meta_error || @dep_errors ) { diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n"; } if ($cpan_meta_error) { my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n"; } if (@dep_errors) { diag join( "\n", "\nThe following REQUIRED prerequisites were not satisfied:\n", @dep_errors, "\n" ); } pass('Reported prereqs'); # vim: ts=4 sts=4 sw=4 et: Gtk3-ImageView-12/t/00-report-prereqs.dd0000644000175000017500000000470214702603320020170 0ustar somebodysomebodydo { my $x = { 'build' => { 'requires' => { 'Glib' => '1.210' } }, 'configure' => { 'requires' => { 'ExtUtils::MakeMaker' => '0' } }, 'develop' => { 'requires' => { 'File::Spec' => '0', 'IO::Handle' => '0', 'IPC::Open3' => '0', 'Test::More' => '0', 'Test::Pod' => '1.41', 'perl' => '5.006' } }, 'runtime' => { 'requires' => { 'Cairo' => '0', 'Carp' => '0', 'Glib' => '1.210', 'Glib::Object::Subclass' => '0', 'Gtk3' => '0', 'List::Util' => '0', 'Readonly' => '0', 'Scalar::Util' => '0', 'base' => '0', 'if' => '0', 'strict' => '0', 'warnings' => '0' } }, 'test' => { 'recommends' => { 'CPAN::Meta' => '2.120900' }, 'requires' => { 'Carp::Always' => '0', 'ExtUtils::MakeMaker' => '0', 'File::Spec' => '0', 'File::Temp' => '0', 'Image::Magick' => '0', 'MIME::Base64' => '0', 'Test::Deep' => '0', 'Test::MockObject' => '0', 'Test::More' => '0', 'Try::Tiny' => '0' } } }; $x; }Gtk3-ImageView-12/Makefile.PL0000644000175000017500000000374014702603320016160 0ustar somebodysomebody# This Makefile.PL for Gtk3-ImageView was generated by # Dist::Zilla::Plugin::MakeMaker::Awesome 0.49. # Don't edit it but the dist.ini and plugins used to construct it. use strict; use warnings; use ExtUtils::MakeMaker; my %WriteMakefileArgs = ( "ABSTRACT" => "Image viewer widget for Gtk3", "AUTHOR" => "Jeffrey Ratcliffe , Alexey Sokolov", "BUILD_REQUIRES" => { "Glib" => "1.210" }, "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "Gtk3-ImageView", "LICENSE" => "perl", "NAME" => "Gtk3::ImageView", "PREREQ_PM" => { "Cairo" => 0, "Carp" => 0, "Glib" => "1.210", "Glib::Object::Subclass" => 0, "Gtk3" => 0, "List::Util" => 0, "Readonly" => 0, "Scalar::Util" => 0, "base" => 0, "if" => 0, "strict" => 0, "warnings" => 0 }, "TEST_REQUIRES" => { "Carp::Always" => 0, "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "File::Temp" => 0, "Image::Magick" => 0, "MIME::Base64" => 0, "Test::Deep" => 0, "Test::MockObject" => 0, "Test::More" => 0, "Try::Tiny" => 0 }, "VERSION" => 12, "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "Cairo" => 0, "Carp" => 0, "Carp::Always" => 0, "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "File::Temp" => 0, "Glib" => "1.210", "Glib::Object::Subclass" => 0, "Gtk3" => 0, "Image::Magick" => 0, "List::Util" => 0, "MIME::Base64" => 0, "Readonly" => 0, "Scalar::Util" => 0, "Test::Deep" => 0, "Test::MockObject" => 0, "Test::More" => 0, "Try::Tiny" => 0, "base" => 0, "if" => 0, "strict" => 0, "warnings" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION('6.63_03') } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); Gtk3-ImageView-12/lib/0000755000175000017500000000000014702603320014750 5ustar somebodysomebodyGtk3-ImageView-12/lib/Gtk3/0000755000175000017500000000000014702603320015560 5ustar somebodysomebodyGtk3-ImageView-12/lib/Gtk3/ImageView.pm0000644000175000017500000007044714702603320020007 0ustar somebodysomebodypackage Gtk3::ImageView; use warnings; use strict; no if $] >= 5.018, warnings => 'experimental::smartmatch'; use Cairo; use Glib qw(TRUE FALSE); # To get TRUE and FALSE use Gtk3; use Gtk3::ImageView::Tool; use Gtk3::ImageView::Tool::Dragger; use Gtk3::ImageView::Tool::Selector; use List::Util qw(min); use Scalar::Util qw(blessed); use Carp; use Readonly; Readonly my $MAX_ZOOM => 100; our $VERSION = '12'; use Glib::Object::Subclass Gtk3::DrawingArea::, signals => { 'zoom-changed' => { param_types => ['Glib::Float'], # new zoom }, 'offset-changed' => { param_types => [ 'Glib::Int', 'Glib::Int' ], # new offset }, 'selection-changed' => { param_types => ['Glib::Scalar'], # Gdk::Rectangle of selection area }, 'tool-changed' => { param_types => ['Glib::Scalar'], # new Gtk3::ImageView::Tool }, 'dnd-start' => { param_types => [ 'Glib::Float', # x 'Glib::Float', # y 'Glib::UInt', # button ], return_type => 'Glib::Boolean', flags => ['run-last'], } }, properties => [ Glib::ParamSpec->object( 'pixbuf', # name 'pixbuf', # nickname 'Gtk3::Gdk::Pixbuf to be shown', # blurb 'Gtk3::Gdk::Pixbuf', [qw/readable writable/] # flags ), Glib::ParamSpec->scalar( 'offset', # name 'Image offset', # nick 'Gdk::Rectangle hash of x, y', # blurb [qw/readable writable/] # flags ), Glib::ParamSpec->float( 'zoom', # name 'zoom', # nick 'zoom level', # blurb 0.001, # minimum 100.0, # maximum 1.0, # default_value [qw/readable writable/] # flags ), Glib::ParamSpec->float( 'zoom-step', # name 'Zoom step', # nick 'Zoom coefficient for every scrolling step', # blurb 1.0, # minimum 10.0, # maximum 2.0, # default_value [qw/readable writable/] # flags ), Glib::ParamSpec->float( 'resolution-ratio', # name 'resolution-ratio', # nick 'Ratio of x-resolution/y-resolution', # blurb 0.0001, # minimum 1000.0, # maximum 1.0, # default_value [qw/readable writable/] # flags ), Glib::ParamSpec->scalar( 'tool', # name 'tool', # nickname 'Active Gtk3::ImageView::Tool', # blurb [qw/readable writable/] # flags ), Glib::ParamSpec->scalar( 'selection', # name 'Selection', # nick 'Gdk::Rectangle hash of selected region', # blurb [qw/readable writable/] # flags ), Glib::ParamSpec->scalar( 'selection-float', # name 'Selection float', # nick 'Gdk::Rectangle hash of selected region (floating point)', # blurb [qw/readable writable/] # flags ), Glib::ParamSpec->boolean( 'zoom-to-fit', # name 'Zoom to fit', # nickname 'Whether the zoom factor is automatically calculated to fit the window' , # blurb TRUE, # default [qw/readable writable/] # flags ), Glib::ParamSpec->float( 'zoom-to-fit-limit', # name 'Zoom to fit limit', # nickname 'When zooming automatically, don\'t zoom more than this', # blurb 0.0001, # minimum 100.0, # maximum 100.0, # default_value [qw/readable writable/] # flags ), Glib::ParamSpec->string( 'interpolation', # name 'interpolation', # nick 'Interpolation method to use, from Cairo::Filter', # blurb 'good', # default [qw/readable writable/], # flags ), ]; sub INIT_INSTANCE { my $self = shift; $self->signal_connect( draw => \&_draw ); $self->signal_connect( 'button-press-event' => \&_button_pressed ); $self->signal_connect( 'button-release-event' => \&_button_released ); $self->signal_connect( 'motion-notify-event' => \&_motion ); $self->signal_connect( 'scroll-event' => \&_scroll ); $self->signal_connect( configure_event => \&_configure_event ); $self->set_app_paintable(TRUE); if ( $Glib::Object::Introspection::VERSION < 0.043 ## no critic (ProhibitMagicNumbers) ) { $self->add_events( ${ Gtk3::Gdk::EventMask->new(qw/exposure-mask/) } | ${ Gtk3::Gdk::EventMask->new(qw/button-press-mask/) } | ${ Gtk3::Gdk::EventMask->new(qw/button-release-mask/) } | ${ Gtk3::Gdk::EventMask->new(qw/pointer-motion-mask/) } | ${ Gtk3::Gdk::EventMask->new(qw/scroll-mask/) } ); } else { $self->add_events( Glib::Object::Introspection->convert_sv_to_flags( 'Gtk3::Gdk::EventMask', 'exposure-mask' ) | Glib::Object::Introspection->convert_sv_to_flags( 'Gtk3::Gdk::EventMask', 'button-press-mask' ) | Glib::Object::Introspection->convert_sv_to_flags( 'Gtk3::Gdk::EventMask', 'button-release-mask' ) | Glib::Object::Introspection->convert_sv_to_flags( 'Gtk3::Gdk::EventMask', 'pointer-motion-mask' ) | Glib::Object::Introspection->convert_sv_to_flags( 'Gtk3::Gdk::EventMask', 'scroll-mask' ) ); } $self->set_tool( Gtk3::ImageView::Tool::Dragger->new($self) ); $self->set_redraw_on_allocate(FALSE); return $self; } sub SET_PROPERTY { my ( $self, $pspec, $newval ) = @_; my $name = $pspec->get_name; my $oldval = $self->get($name); my $invalidate = FALSE; if ( ( defined $newval and defined $oldval and $newval ne $oldval ) or ( defined $newval xor defined $oldval ) ) { if ( $name eq 'pixbuf' ) { $self->{$name} = $newval; $invalidate = TRUE; } elsif ( $name eq 'zoom' ) { $self->{$name} = $newval; $self->signal_emit( 'zoom-changed', $newval ); $invalidate = TRUE; } elsif ( $name eq 'offset' ) { if ( ( defined $newval xor defined $oldval ) or $oldval->{x} != $newval->{x} or $oldval->{y} != $newval->{y} ) { $self->{$name} = $newval; $self->signal_emit( 'offset-changed', $newval->{x}, $newval->{y} ); $invalidate = TRUE; } } elsif ( $name eq 'resolution_ratio' ) { $self->{$name} = $newval; $invalidate = TRUE; } elsif ( $name eq 'interpolation' ) { $self->{$name} = $newval; $invalidate = TRUE; } elsif ( $name eq 'selection' ) { if ( _selections_nequal( $oldval, $newval ) ) { $self->{$name} = $newval; $self->{selection_float} = $newval; $invalidate = TRUE; $self->signal_emit( 'selection-changed', $newval ); } } elsif ( $name eq 'selection_float' ) { if ( _selections_nequal( $oldval, $newval ) ) { $self->{$name} = $newval; my $x1 = int( $newval->{x} + 0.5 ); my $y1 = int( $newval->{y} + 0.5 ); my $x2 = int( $newval->{x} + $newval->{width} + 0.5 ); my $y2 = int( $newval->{y} + $newval->{height} + 0.5 ); $self->{selection} = { x => $x1, y => $y1, width => $x2 - $x1, height => $y2 - $y1, }; # Even for the float selection, w/h should be int; otherwise dragging the selection looks funny $self->{$name}->{width} = $x2 - $x1; $self->{$name}->{height} = $y2 - $y1; $invalidate = TRUE; $self->signal_emit( 'selection-changed', $newval ); } } elsif ( $name eq 'tool' ) { $self->{$name} = $newval; if ( defined $self->get_selection ) { $invalidate = TRUE; } $self->signal_emit( 'tool-changed', $newval ); } else { $self->{$name} = $newval; # $self->SUPER::SET_PROPERTY( $pspec, $newval ); } if ($invalidate) { $self->queue_draw(); } } return; } sub _selections_nequal { my ( $oldval, $newval ) = @_; return ( ( defined $newval xor defined $oldval ) or $oldval->{x} != $newval->{x} or $oldval->{y} != $newval->{y} or $oldval->{width} != $newval->{width} or $oldval->{height} != $newval->{height} ); } sub set_pixbuf { my ( $self, $pixbuf, $zoom_to_fit ) = @_; $self->set( 'pixbuf', $pixbuf ); $self->set_zoom_to_fit($zoom_to_fit); if ( not $zoom_to_fit ) { $self->set_offset( 0, 0 ); } return; } sub get_pixbuf { my ($self) = @_; return $self->get('pixbuf'); } sub get_pixbuf_size { my ($self) = @_; my $pixbuf = $self->get_pixbuf; if ( defined $pixbuf ) { return { width => $pixbuf->get_width, height => $pixbuf->get_height }; } return; } sub _button_pressed { my ( $self, $event ) = @_; return $self->get_tool->button_pressed($event); } sub _button_released { my ( $self, $event ) = @_; $self->get_tool->button_released($event); return; } sub _motion { my ( $self, $event ) = @_; $self->update_cursor( $event->x, $event->y ); $self->get_tool->motion($event); return; } sub _scroll { my ( $self, $event ) = @_; my ( $center_x, $center_y ) = $self->to_image_coords( $event->x, $event->y ); my $zoom; $self->set_zoom_to_fit(FALSE); if ( $event->direction eq 'up' ) { $zoom = $self->get_zoom * $self->get('zoom-step'); } else { $zoom = $self->get_zoom / $self->get('zoom-step'); } $self->_set_zoom_with_center( $zoom, $center_x, $center_y ); return; } sub _draw { my ( $self, $context ) = @_; my $allocation = $self->get_allocation; my $style = $self->get_style_context; my $pixbuf = $self->get_pixbuf; my $ratio = $self->get_resolution_ratio; my $viewport = $self->get_viewport; $style->add_class('imageview'); $style->save; $style->add_class(Gtk3::STYLE_CLASS_BACKGROUND); Gtk3::render_background( $style, $context, $allocation->{x}, $allocation->{y}, $allocation->{width}, $allocation->{height} ); $style->restore; if ( defined $pixbuf ) { if ( $pixbuf->get_has_alpha ) { $style->save; # '.imageview' affects also area outside of the image. But only # when background-image is specified. background-color seems to # have no effect there. Probably a bug in Gtk? Either way, this is # why need a special class 'transparent' to match the correct area # inside the image where both image and color work. $style->add_class('transparent'); my ( $x1, $y1 ) = $self->to_widget_coords( 0, 0 ); my ( $x2, $y2 ) = $self->to_widget_coords( $pixbuf->get_width, $pixbuf->get_height ); Gtk3::render_background( $style, $context, $x1, $y1, $x2 - $x1, $y2 - $y1 ); $style->restore; } my $zoom = $self->get_zoom / $self->get('scale-factor'); $context->scale( $zoom / $ratio, $zoom ); my $offset = $self->get_offset; $context->translate( $offset->{x}, $offset->{y} ); Gtk3::Gdk::cairo_set_source_pixbuf( $context, $pixbuf, 0, 0 ); $context->get_source->set_filter( $self->get_interpolation ); } else { my $bgcol = $style->get( 'normal', 'background-color' ); Gtk3::Gdk::cairo_set_source_rgba( $context, $bgcol ); } $context->paint; my $selection = $self->get_selection; if ( defined $pixbuf and defined $selection ) { my ( $x, $y, $w, $h, ) = ( $selection->{x}, $selection->{y}, $selection->{width}, $selection->{height}, ); if ( $w <= 0 or $h <= 0 ) { return TRUE } $style->save; $style->add_class(Gtk3::STYLE_CLASS_RUBBERBAND); Gtk3::render_background( $style, $context, $x, $y, $w, $h ); Gtk3::render_frame( $style, $context, $x, $y, $w, $h ); $style->restore; } return TRUE; } sub _configure_event { my ( $self, $event ) = @_; if ( $self->get_zoom_to_fit ) { $self->zoom_to_box( $self->get_pixbuf_size ); } return; } # setting the zoom via the public API disables zoom-to-fit sub set_zoom { my ( $self, $zoom ) = @_; $self->set_zoom_to_fit(FALSE); $self->_set_zoom_no_center($zoom); return; } sub _set_zoom { my ( $self, $zoom ) = @_; if ( $zoom > $MAX_ZOOM ) { $zoom = $MAX_ZOOM } $self->set( 'zoom', $zoom ); return; } sub get_zoom { my ($self) = @_; return $self->get('zoom'); } # convert x, y in image coords to widget coords sub to_widget_coords { my ( $self, $x, $y ) = @_; my $zoom = $self->get_zoom; my $ratio = $self->get_resolution_ratio; my $offset = $self->get_offset; my $factor = $self->get('scale-factor'); return ( $x + $offset->{x} ) * $zoom / $factor / $ratio, ( $y + $offset->{y} ) * $zoom / $factor; } # convert x, y in widget coords to image coords sub to_image_coords { my ( $self, $x, $y ) = @_; my $zoom = $self->get_zoom; my $ratio = $self->get_resolution_ratio; my $offset = $self->get_offset; my $factor = $self->get('scale-factor'); return $x * $factor / $zoom * $ratio - $offset->{x}, $y * $factor / $zoom - $offset->{y}; } # convert x, y in widget distance to image distance sub to_image_distance { my ( $self, $x, $y ) = @_; my $zoom = $self->get_zoom; my $ratio = $self->get_resolution_ratio; my $factor = $self->get('scale-factor'); return $x * $factor / $zoom * $ratio, $y * $factor / $zoom; } # set zoom with centre in image coordinates sub _set_zoom_with_center { my ( $self, $zoom, $center_x, $center_y ) = @_; my $allocation = $self->get_allocation; my $ratio = $self->get_resolution_ratio; my $factor = $self->get('scale-factor'); my $offset_x = $allocation->{width} * $factor / 2 / $zoom * $ratio - $center_x; my $offset_y = $allocation->{height} * $factor / 2 / $zoom - $center_y; $self->_set_zoom($zoom); $self->set_offset( $offset_x, $offset_y ); return; } # sets zoom, centred on the viewport sub _set_zoom_no_center { my ( $self, $zoom ) = @_; my $allocation = $self->get_allocation; my ( $center_x, $center_y ) = $self->to_image_coords( $allocation->{width} / 2, $allocation->{height} / 2 ); $self->_set_zoom_with_center( $zoom, $center_x, $center_y ); return; } sub set_zoom_to_fit { my ( $self, $zoom_to_fit, $limit ) = @_; $self->set( 'zoom-to-fit', $zoom_to_fit ); if ( defined $limit ) { $self->set( 'zoom-to-fit-limit', $limit ); } if ( not $zoom_to_fit ) { return } $self->zoom_to_box( $self->get_pixbuf_size ); return; } sub zoom_to_box { my ( $self, $box, $additional_factor ) = @_; if ( not defined $box ) { return } if ( not defined $box->{x} ) { $box->{x} = 0 } if ( not defined $box->{y} ) { $box->{y} = 0 } if ( not defined $additional_factor ) { $additional_factor = 1 } my $allocation = $self->get_allocation; my $ratio = $self->get_resolution_ratio; my $limit = $self->get('zoom-to-fit-limit'); my $sc_factor_w = min( $limit, $allocation->{width} / $box->{width} ) * $ratio; my $sc_factor_h = min( $limit, $allocation->{height} / $box->{height} ); $self->_set_zoom_with_center( min( $sc_factor_w, $sc_factor_h ) * $additional_factor * $self->get('scale-factor'), ( $box->{x} + $box->{width} / 2 ) / $ratio, $box->{y} + $box->{height} / 2 ); return; } sub zoom_to_selection { my ( $self, $context_factor ) = @_; $self->zoom_to_box( $self->get_selection, $context_factor ); return; } sub get_zoom_to_fit { my ($self) = @_; return $self->get('zoom-to-fit'); } sub zoom_in { my ($self) = @_; $self->set_zoom_to_fit(FALSE); $self->_set_zoom_no_center( $self->get_zoom * $self->get('zoom-step') ); return; } sub zoom_out { my ($self) = @_; $self->set_zoom_to_fit(FALSE); $self->_set_zoom_no_center( $self->get_zoom / $self->get('zoom-step') ); return; } sub zoom_to_fit { my ($self) = @_; $self->set_zoom_to_fit(TRUE); return; } sub set_fitting { my ( $self, $value ) = @_; $self->set_zoom_to_fit( $value, 1 ); return; } sub _clamp_direction { my ( $offset, $allocation, $pixbuf_size ) = @_; # Centre the image if it is smaller than the widget if ( $allocation > $pixbuf_size ) { $offset = ( $allocation - $pixbuf_size ) / 2; } # Otherwise don't allow the LH/top edge of the image to be visible elsif ( $offset > 0 ) { $offset = 0; } # Otherwise don't allow the RH/bottom edge of the image to be visible elsif ( $offset < $allocation - $pixbuf_size ) { $offset = $allocation - $pixbuf_size; } return $offset; } sub set_offset { my ( $self, $offset_x, $offset_y ) = @_; if ( not defined $self->get_pixbuf ) { return } # Convert the widget size to image scale to make the comparisons easier my $allocation = $self->get_allocation; ( $allocation->{width}, $allocation->{height} ) = $self->to_image_distance( $allocation->{width}, $allocation->{height} ); my $pixbuf_size = $self->get_pixbuf_size; $offset_x = _clamp_direction( $offset_x, $allocation->{width}, $pixbuf_size->{width} ); $offset_y = _clamp_direction( $offset_y, $allocation->{height}, $pixbuf_size->{height} ); $self->set( 'offset', { x => $offset_x, y => $offset_y } ); return; } sub get_offset { my ($self) = @_; return $self->get('offset'); } sub get_viewport { my ($self) = @_; my $allocation = $self->get_allocation; my $pixbuf = $self->get_pixbuf; my ( $x, $y, $w, $h ); if ( defined $pixbuf ) { ( $x, $y, $w, $h ) = ( $self->to_image_coords( 0, 0 ), $self->to_image_distance( $allocation->{width}, $allocation->{height} ) ); } else { ( $x, $y, $w, $h ) = ( 0, 0, $allocation->{width}, $allocation->{height} ); } return { x => $x, y => $y, width => $w, height => $h }; } sub set_tool { my ( $self, $tool ) = @_; if ( not( blessed $tool and $tool->isa('Gtk3::ImageView::Tool') ) ) { # TODO remove this fallback, only accept Tool directly if ( $tool eq 'dragger' ) { $tool = Gtk3::ImageView::Tool::Dragger->new($self); } elsif ( $tool eq 'selector' ) { $tool = Gtk3::ImageView::Tool::Selector->new($self); } else { croak 'invalid set_tool call'; } } $self->set( 'tool', $tool ); return; } sub get_tool { my ($self) = @_; return $self->get('tool'); } sub set_selection { my ( $self, $selection ) = @_; my $pixbuf_size = $self->get_pixbuf_size; if ( not defined $pixbuf_size ) { return } if ( $selection->{x} < 0 ) { $selection->{width} += $selection->{x}; $selection->{x} = 0; } if ( $selection->{y} < 0 ) { $selection->{height} += $selection->{y}; $selection->{y} = 0; } if ( $selection->{x} + $selection->{width} > $pixbuf_size->{width} ) { $selection->{width} = $pixbuf_size->{width} - $selection->{x}; } if ( $selection->{y} + $selection->{height} > $pixbuf_size->{height} ) { $selection->{height} = $pixbuf_size->{height} - $selection->{y}; } $self->set( 'selection-float', $selection ); return; } sub get_selection { my ($self) = @_; return $self->get('selection'); } sub set_resolution_ratio { my ( $self, $ratio ) = @_; $self->set( 'resolution-ratio', $ratio ); if ( $self->get_zoom_to_fit ) { $self->zoom_to_box( $self->get_pixbuf_size ); } return; } sub get_resolution_ratio { my ($self) = @_; return $self->get('resolution-ratio'); } sub update_cursor { my ( $self, $x, $y ) = @_; my $pixbuf_size = $self->get_pixbuf_size; if ( not defined $pixbuf_size ) { return } my $win = $self->get_window; my $cursor = $self->get_tool->cursor_at_point( $x, $y ); if ( defined $cursor ) { $win->set_cursor($cursor); } return; } sub set_interpolation { my ( $self, $interpolation ) = @_; $self->set( 'interpolation', $interpolation ); return; } sub get_interpolation { my ($self) = @_; return $self->get('interpolation'); } 1; __END__ =encoding utf8 =head1 NAME Gtk3::ImageView - Image viewer widget for Gtk3 =head1 VERSION 9 =head1 SYNOPSIS use Gtk3::ImageView; Gtk3->init; $window = Gtk3::Window->new(); $view = Gtk3::ImageView->new; $view->set_pixbuf($pixbuf, TRUE); $window->add($view); $window->show_all; =head1 DESCRIPTION The Gtk3::ImageView widget allows the user to zoom, pan and select the specified image and provides hooks to allow additional tools, e.g. painter, to be created and used. Gtk3::ImageView is a Gtk3 port of L. To discuss Gtk3::ImageView or gtk3-perl, ask questions and flame/praise the authors, join gtk-perl-list@gnome.org at lists.gnome.org. =for readme stop =head1 SUBROUTINES/METHODS =head2 $view->set_pixbuf( $pixbuf, $zoom_to_fit ) Defines the image to view. The optional zoom_to_fit parameter specifies whether to zoom to fit the image or not. =head2 $view->get_pixbuf Returns the image currently being viewed. =head2 $view->get_pixbuf_size Returns a hash of containing the size of the current image in width and height keys. =head2 $view->set_zoom($zoom) Specifies the zoom level. =head2 $view->get_zoom Returns the current zoom level. =head2 $view->set_zoom_to_fit($zoom_to_fit, $limit) Specifies whether to zoom to fit or not. If limit is defined, such automatic zoom will not zoom more than it. If the limit is undefined, the previously set limit is used, initially it's unlimited. =head2 $view->zoom_to_box( $box, $additional_factor ) Specifies a box to zoom to, including an additional zoom factor =head2 $view->zoom_to_selection($context_factor) Zooms to the current selection, plus an addition zoom factor. Shortcut for $view->zoom_to_box( $view->get_selection, $context_factor ); =head2 $view->get_zoom_to_fit Returns whether the view is currently zoom to fit or not. =head2 $view->zoom_in Increases the current zoom by C times (defaults to 2). =head2 $view->zoom_out Decreases the current zoom by C times (defaults to 2). =head2 $view->zoom_to_fit Shortcut for $view->set_zoom_to_fit(TRUE); =head2 $view->set_fitting( $value ) Shortcut for $view->set_zoom_to_fit($value, 1); which means that it won't stretch a small image to a large window. Provided (despite the ambiguous name) for compatibility with Gtk2::ImageView. =head2 $view->set_offset( $x, $y ) Set the current view offset (i.e. pan position). =head2 $view->set_offset Returns the current view offset (i.e. pan position). =head2 $view->get_viewport Returns a hash containing the position and size of the current viewport. =head2 $view->set_tool Set the current tool (i.e. mode) - an object of a subclass of C. Here are some known subclasses of it: =over 1 =item * C allows the image to be dragged when zoomed. =item * C allows the user to select a rectangular area of the image. =item * C selects or drags with different mouse buttons. =back Don't rely too much on how Tool currently interacts with ImageView. =head2 $view->get_tool Returns the current tool (i.e. mode). =head2 $view->set_selection($selection) Set the current selection as a hash of position and size. =head2 $view->get_selection Returns the current selection as a hash of position and size. =head2 $view->set_resolution_ratio($ratio) Set the ratio of the resolutions in the x and y directions, allowing images with non-square pixels to be correctly displayed. =head2 $view->get_resolution_ratio Returns the current resolution ratio. =head2 $view->set_interpolation The interpolation method to use, from L|https://www.cairographics.org/manual/cairo-cairo-pattern-t.html#cairo-filter-t> type. Possible values are lowercase strings like C. To use different interpolation depending on zoom, set it in the C signal. =head2 $view->get_interpolation Returns the current interpolation method. =head1 DIAGNOSTICS =head1 CONFIGURATION AND ENVIRONMENT =head1 DEPENDENCIES =head1 INCOMPATIBILITIES =head2 Porting from L =over 1 =item * C was renamed to C and now its second argument means C instead of C. =item * C used to be the default, now you need to call it explicitly if you want that behavior. However, once it's called, new calls to C won't reset it, see C for more details.. =item * Drag and drop now can be triggered by subscribing to C signal, and calling C<$view-Edrag_begin_with_coordinates()> from the handler. C won't work. =item * C replacement is not yet implemented. =item * C is now available through L instead, e.g. via .imageview.transparent { background-image: url('checkers.svg'); } =item * C now accepts L|https://www.cairographics.org/manual/cairo-cairo-pattern-t.html#cairo-filter-t> instead of L|https://developer.gnome.org/gdk-pixbuf/stable/gdk-pixbuf-Scaling.html#GdkInterpType>. =back =head1 BUGS AND LIMITATIONS This should be rewritten on C, and Perl bindings should be provided via Glib Object Introspection. =head1 AUTHOR Jeffrey Ratcliffe, Ejffry@posteo.netE Alexey Sokolov Esokolov@google.comE =head1 LICENSE AND COPYRIGHT Copyright (C) 2018--2020 by Jeffrey Ratcliffe Copyright (C) 2020 by Google LLC Modelled after the GtkImageView C widget by Björn Lindqvist This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.5 or, at your option, any later version of Perl 5 you may have available. =cut Gtk3-ImageView-12/lib/Gtk3/ImageView/0000755000175000017500000000000014702603320017435 5ustar somebodysomebodyGtk3-ImageView-12/lib/Gtk3/ImageView/Tool.pm0000644000175000017500000000223414702603320020711 0ustar somebodysomebodypackage Gtk3::ImageView::Tool; use warnings; use strict; use Glib qw(TRUE FALSE); # To get TRUE and FALSE our $VERSION = '12'; sub new { my $class = shift; my $view = shift; return bless { _view => $view, }, $class; } sub view { my $self = shift; return $self->{_view}; } sub button_pressed { my $self = shift; my $event = shift; return FALSE; } sub button_released { my $self = shift; my $event = shift; return FALSE; } sub motion { my $self = shift; my $event = shift; return FALSE; } sub cursor_at_point { my ( $self, $x, $y ) = @_; my $display = Gtk3::Gdk::Display::get_default; my $cursor_type = $self->cursor_type_at_point( $x, $y ); if ( defined $cursor_type ) { return Gtk3::Gdk::Cursor->new_from_name( $display, $cursor_type ); } return; } sub cursor_type_at_point { my ( $self, $x, $y ) = @_; return; } # compatibility layer sub signal_connect { my ( $self, @args ) = @_; return $self->view->signal_connect(@args); } sub signal_handler_disconnect { my ( $self, @args ) = @_; return $self->view->signal_handler_disconnect(@args); } 1; Gtk3-ImageView-12/lib/Gtk3/ImageView/Tool/0000755000175000017500000000000014702603320020352 5ustar somebodysomebodyGtk3-ImageView-12/lib/Gtk3/ImageView/Tool/Dragger.pm0000644000175000017500000000544514702603320022273 0ustar somebodysomebodypackage Gtk3::ImageView::Tool::Dragger; use warnings; use strict; use base 'Gtk3::ImageView::Tool'; use Glib qw(TRUE FALSE); # To get TRUE and FALSE use Readonly; Readonly my $FLOAT_EPS => 0.01; Readonly my $RIGHT_BUTTON => 3; our $VERSION = '12'; sub button_pressed { my $self = shift; my $event = shift; # Don't block context menu if ( $event->button == $RIGHT_BUTTON ) { return FALSE; } $self->{drag_start} = { x => $event->x, y => $event->y }; $self->{dnd_start} = { x => $event->x, y => $event->y }; $self->{dnd_eligible} = TRUE; $self->{dragging} = TRUE; $self->{button} = $event->button; $self->view->update_cursor( $event->x, $event->y ); return TRUE; } sub button_released { my $self = shift; my $event = shift; $self->{dragging} = FALSE; $self->view->update_cursor( $event->x, $event->y ); return; } sub motion { my $self = shift; my $event = shift; if ( not $self->{dragging} ) { return FALSE } my $offset = $self->view->get_offset; my $zoom = $self->view->get_zoom; my $ratio = $self->view->get_resolution_ratio; my $offset_x = $offset->{x} + ( $event->x - $self->{drag_start}{x} ) / $zoom * $ratio; my $offset_y = $offset->{y} + ( $event->y - $self->{drag_start}{y} ) / $zoom; ( $self->{drag_start}{x}, $self->{drag_start}{y} ) = ( $event->x, $event->y ); $self->view->set_offset( $offset_x, $offset_y ); my $new_offset = $self->view->get_offset; if ( not $self->{dnd_eligible} ) { return; } if ( _approximately( $new_offset->{x}, $offset_x ) and _approximately( $new_offset->{y}, $offset_y ) ) { # If there was a movement in the image, disable start of dnd until # mouse button is pressed again $self->{dnd_eligible} = FALSE; return; } # movement was clamped because of the edge, but did mouse move far enough? if ( $self->view->drag_check_threshold( $self->{dnd_start}{x}, $self->{dnd_start}{y}, $event->x, $event->y ) and $self->view->signal_emit( 'dnd-start', $event->x, $event->y, $self->{button} ) ) { $self->{dragging} = FALSE; } return; } sub _approximately { my ( $a, $b ) = @_; return abs( $a - $b ) < $FLOAT_EPS; } sub cursor_type_at_point { my ( $self, $x, $y ) = @_; ( $x, $y ) = $self->view->to_image_coords( $x, $y ); my $pixbuf_size = $self->view->get_pixbuf_size; if ( $x > 0 and $x < $pixbuf_size->{width} and $y > 0 and $y < $pixbuf_size->{height} ) { if ( $self->{dragging} ) { return 'grabbing'; } else { return 'grab'; } } return; } 1; Gtk3-ImageView-12/lib/Gtk3/ImageView/Tool/Selector.pm0000644000175000017500000001300114702603320022463 0ustar somebodysomebodypackage Gtk3::ImageView::Tool::Selector; use warnings; use strict; use base 'Gtk3::ImageView::Tool'; use Glib qw(TRUE FALSE); # To get TRUE and FALSE use List::Util qw(min); use Readonly; Readonly my $RIGHT_BUTTON => 3; Readonly my $EDGE_WIDTH => 5; our $VERSION = '12'; sub button_pressed { my ( $self, $event ) = @_; if ( $event->button == $RIGHT_BUTTON ) { return FALSE; } my $type = $self->cursor_type_at_point( $event->x, $event->y ); if ( $type eq 'grab' ) { $type = 'grabbing'; $self->{drag_start_x} = int( $event->x + 0.5 ); $self->{drag_start_y} = int( $event->y + 0.5 ); } $self->{dragging} = $type; $self->_update_selection( $event->x, $event->y ); return FALSE; } sub button_released { my ( $self, $event ) = @_; if ( $event->button == $RIGHT_BUTTON ) { return FALSE; } if ( $self->{dragging} ) { $self->_update_selection( $event->x, $event->y ); } $self->{dragging} = undef; $self->view->update_cursor( $event->x, $event->y ); return FALSE; } sub motion { my ( $self, $event ) = @_; if ( $self->{dragging} ) { $self->_update_selection( $event->x, $event->y ); } return FALSE; } sub cursor_type_at_point { ## no critic (ProhibitExcessComplexity); my ( $self, $x, $y ) = @_; if ( $self->{dragging} ) { return $self->{dragging}; } my $selection = $self->view->get_selection; if ( !defined $selection ) { return 'crosshair'; } my $edge_width = $EDGE_WIDTH * $self->view->get('scale-factor'); my ( $sx1, $sy1 ) = $self->view->to_widget_coords( $selection->{x}, $selection->{y} ); my ( $sx2, $sy2 ) = $self->view->to_widget_coords( $selection->{x} + $selection->{width}, $selection->{y} + $selection->{height} ); if ( $x < $sx1 - $edge_width || $x > $sx2 + $edge_width || $y < $sy1 - $edge_width || $y > $sy2 + $edge_width ) { return 'crosshair'; } if ( $x > $sx1 + $edge_width && $x < $sx2 - $edge_width && $y > $sy1 + $edge_width && $y < $sy2 - $edge_width ) { return 'grab'; } # This makes it possible for the selection to be smaller than edge_width and still be resizeable in all directions my $leftish = $x < ( $sx1 + $sx2 ) / 2; my $topish = $y < ( $sy1 + $sy2 ) / 2; if ( $y > $sy1 + $edge_width && $y < $sy2 - $edge_width ) { if ($leftish) { return 'w-resize'; } else { return 'e-resize'; } } if ( $x > $sx1 + $edge_width && $x < $sx2 - $edge_width ) { if ($topish) { return 'n-resize'; } else { return 's-resize'; } } if ($leftish) { if ($topish) { return 'nw-resize'; } else { return 'sw-resize'; } } else { if ($topish) { return 'ne-resize'; } else { return 'se-resize'; } } } sub _update_selection { my ( $self, $x, $y ) = @_; my $selection = $self->view->get('selection-float') // { x => 0, y => 0, width => 0, height => 0, }; my ( $sel_x1, $sel_y1 ) = $self->view->to_widget_coords( $selection->{x}, $selection->{y} ); my ( $sel_x2, $sel_y2 ) = $self->view->to_widget_coords( $selection->{x} + $selection->{width}, $selection->{y} + $selection->{height} ); my $type = $self->{dragging}; if ( $type eq 'grabbing' ) { my $off_x = $x - $self->{drag_start_x}; my $off_y = $y - $self->{drag_start_y}; $sel_x1 += $off_x; $sel_x2 += $off_x; $sel_y1 += $off_y; $sel_y2 += $off_y; $self->{drag_start_x} = $x; $self->{drag_start_y} = $y; } if ( $type eq 'crosshair' ) { $sel_x1 = $x; $sel_x2 = $x; $sel_y1 = $y; $sel_y2 = $y; $type = 'se-resize'; } my $flip_we = 0; my $flip_ns = 0; if ( $type =~ /w-resize/smx ) { $sel_x1 = $x; if ( $x > $sel_x2 ) { $flip_we = 'e' } } if ( $type =~ /e-resize/smx ) { $sel_x2 = $x; if ( $x < $sel_x1 ) { $flip_we = 'w' } } if ( $type =~ /n.?-resize/smx ) { $sel_y1 = $y; if ( $y > $sel_y2 ) { $flip_ns = 's' } } if ( $type =~ /s.?-resize/smx ) { $sel_y2 = $y; if ( $y < $sel_y1 ) { $flip_ns = 'n' } } my ( $w, $h ) = $self->view->to_image_distance( abs( $sel_x2 - $sel_x1 ), abs( $sel_y2 - $sel_y1 ) ); my ( $img_x, $img_y ) = $self->view->to_image_coords( min( $sel_x1, $sel_x2 ), min( $sel_y1, $sel_y2 ) ); $self->view->set_selection( { x => $img_x, y => $img_y, width => $w, height => $h, } ); # Prepare for next mouse event # If we are dragging, a corner cursor must stay as a corner cursor, # a left/right cursor must stay as left/right, # and a top/bottom cursor must stay as top/bottom if ($flip_we) { $type =~ s/[we]-/$flip_we-/smx; } if ($flip_ns) { $type =~ s/^[ns]/$flip_ns/smx; } $self->{dragging} = $type; $self->view->update_cursor( $x, $y ); return; } # compatibility layer sub get_selection { my $self = shift; return $self->view->get_selection; } sub set_selection { my ( $self, @args ) = @_; $self->view->set_selection(@args); return; } 1; Gtk3-ImageView-12/lib/Gtk3/ImageView/Tool/SelectorDragger.pm0000644000175000017500000000241414702603320023765 0ustar somebodysomebodypackage Gtk3::ImageView::Tool::SelectorDragger; use warnings; use strict; use base 'Gtk3::ImageView::Tool'; use Glib qw(TRUE FALSE); # To get TRUE and FALSE our $VERSION = '12'; sub new { my $class = shift; my $view = shift; my $self = Gtk3::ImageView::Tool->new($view); $self->{_selector} = Gtk3::ImageView::Tool::Selector->new($view); $self->{_dragger} = Gtk3::ImageView::Tool::Dragger->new($view); $self->{_tool} = $self->{_selector}; return bless $self, $class; } sub button_pressed { my $self = shift; my $event = shift; # left mouse button if ( $event->button == 1 ) { $self->{_tool} = $self->{_selector}; } elsif ( $event->button == 2 ) { # middle mouse button $self->{_tool} = $self->{_dragger}; } else { return FALSE; } return $self->{_tool}->button_pressed($event); } sub button_released { my $self = shift; my $event = shift; $self->{_tool}->button_released($event); $self->{_tool} = $self->{_selector}; return; } sub motion { my $self = shift; my $event = shift; $self->{_tool}->motion($event); return; } sub cursor_type_at_point { my ( $self, $x, $y ) = @_; return $self->{_tool}->cursor_type_at_point( $x, $y ); } 1; Gtk3-ImageView-12/xt/0000755000175000017500000000000014702603320014635 5ustar somebodysomebodyGtk3-ImageView-12/xt/author/0000755000175000017500000000000014702603320016137 5ustar somebodysomebodyGtk3-ImageView-12/xt/author/pod-syntax.t0000644000175000017500000000025214702603320020431 0ustar somebodysomebody#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); Gtk3-ImageView-12/xt/author/00-compile.t0000644000175000017500000000277714702603320020206 0ustar somebodysomebodyuse 5.006; use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::Compile 2.058 use Test::More; plan tests => 6; my @module_files = ( 'Gtk3/ImageView.pm', 'Gtk3/ImageView/Tool.pm', 'Gtk3/ImageView/Tool/Dragger.pm', 'Gtk3/ImageView/Tool/Selector.pm', 'Gtk3/ImageView/Tool/SelectorDragger.pm' ); # no fake home requested my @switches = ( -d 'blib' ? '-Mblib' : '-Ilib', ); use File::Spec; use IPC::Open3; use IO::Handle; open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; my @warnings; for my $lib (@module_files) { # see L my $stderr = IO::Handle->new; diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'} . $str . q{'} } $^X, @switches, '-e', "require q[$lib]")) if $ENV{PERL_COMPILE_TEST_DEBUG}; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-e', "require q[$lib]"); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($?, 0, "$lib loaded ok"); shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/ and not eval { +require blib; blib->VERSION('1.01') }; if (@_warnings) { warn @_warnings; push @warnings, @_warnings; } } is(scalar(@warnings), 0, 'no warnings found') or diag 'got warnings: ', ( Test::More->can('explain') ? Test::More::explain(\@warnings) : join("\n", '', @warnings) );