From f897f2aa7055c493db6391c50c8d19da970078e8 Mon Sep 17 00:00:00 2001 From: Andreas Brachold Date: Tue, 19 Jul 2005 15:09:05 +0000 Subject: Initial import with release 0.2.3 --- COPYING | 340 +++++++ HISTORY | 217 +++++ Makefile | 130 +++ README | 148 +++ README.DE | 152 +++ TODO | 7 + commands.c | 239 +++++ commands.h | 64 ++ contrib/image_pregen.sh | 46 + contrib/install-scripts.sh | 35 + control-image.c | 1137 +++++++++++++++++++++ control-image.h | 169 ++++ data-image.c | 266 +++++ data-image.h | 87 ++ data.c | 508 ++++++++++ data.h | 128 +++ examples/imagecmds.conf | 22 + examples/imagecmds.conf.DE | 16 + examples/imagesources.conf | 21 + i18n.c | 625 ++++++++++++ i18n.h | 30 + image.c | 129 +++ image.h | 67 ++ libimage/Makefile | 51 + libimage/error.svg | 2167 +++++++++++++++++++++++++++++++++++++++++ libimage/error.xpm | 454 +++++++++ libimage/error_small.xpm | 183 ++++ libimage/img1.xpm | 56 ++ libimage/img2.xpm | 56 ++ libimage/img3.xpm | 56 ++ libimage/img4.xpm | 56 ++ libimage/img5.xpm | 56 ++ libimage/img6.xpm | 56 ++ libimage/img7.xpm | 56 ++ libimage/img8.xpm | 56 ++ libimage/img9.xpm | 56 ++ libimage/pnm.c | 512 ++++++++++ libimage/pnm.h | 218 +++++ libimage/xpm.c | 224 +++++ libimage/xpm.h | 65 ++ liboutput/Makefile | 58 ++ liboutput/encode.c | 369 +++++++ liboutput/encode.h | 92 ++ liboutput/stillimage-player.c | 61 ++ liboutput/stillimage-player.h | 55 ++ liboutput/stillimage.c | 210 ++++ liboutput/stillimage.h | 54 + list.c | 221 +++++ list.h | 60 ++ menu-commands.c | 163 ++++ menu-commands.h | 60 ++ menu-image.c | 115 +++ menu-image.h | 41 + menu.c | 402 ++++++++ menu.h | 75 ++ player-image.c | 478 +++++++++ player-image.h | 148 +++ scripts/imageplugin.sh | 160 +++ scripts/mount.sh | 35 + setup-image.c | 145 +++ setup-image.h | 73 ++ 61 files changed, 12036 insertions(+) create mode 100644 COPYING create mode 100644 HISTORY create mode 100644 Makefile create mode 100644 README create mode 100644 README.DE create mode 100644 TODO create mode 100644 commands.c create mode 100644 commands.h create mode 100755 contrib/image_pregen.sh create mode 100755 contrib/install-scripts.sh create mode 100644 control-image.c create mode 100644 control-image.h create mode 100644 data-image.c create mode 100644 data-image.h create mode 100644 data.c create mode 100644 data.h create mode 100644 examples/imagecmds.conf create mode 100644 examples/imagecmds.conf.DE create mode 100644 examples/imagesources.conf create mode 100644 i18n.c create mode 100644 i18n.h create mode 100644 image.c create mode 100644 image.h create mode 100644 libimage/Makefile create mode 100644 libimage/error.svg create mode 100644 libimage/error.xpm create mode 100644 libimage/error_small.xpm create mode 100644 libimage/img1.xpm create mode 100644 libimage/img2.xpm create mode 100644 libimage/img3.xpm create mode 100644 libimage/img4.xpm create mode 100644 libimage/img5.xpm create mode 100644 libimage/img6.xpm create mode 100644 libimage/img7.xpm create mode 100644 libimage/img8.xpm create mode 100644 libimage/img9.xpm create mode 100644 libimage/pnm.c create mode 100644 libimage/pnm.h create mode 100644 libimage/xpm.c create mode 100644 libimage/xpm.h create mode 100644 liboutput/Makefile create mode 100644 liboutput/encode.c create mode 100644 liboutput/encode.h create mode 100644 liboutput/stillimage-player.c create mode 100644 liboutput/stillimage-player.h create mode 100644 liboutput/stillimage.c create mode 100644 liboutput/stillimage.h create mode 100644 list.c create mode 100644 list.h create mode 100644 menu-commands.c create mode 100644 menu-commands.h create mode 100644 menu-image.c create mode 100644 menu-image.h create mode 100644 menu.c create mode 100644 menu.h create mode 100644 player-image.c create mode 100644 player-image.h create mode 100755 scripts/imageplugin.sh create mode 100755 scripts/mount.sh create mode 100644 setup-image.c create mode 100644 setup-image.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU 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. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), 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 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 show them these terms so they know 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. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + 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 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 derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 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 License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +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. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary 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 + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 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 Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing 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 for copying, distributing or modifying +the Program or works based on it. + + 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. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. 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 this 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 +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. 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 + + 11. 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. + + 12. 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 + + 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 the public, 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) + + 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) year 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 is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/HISTORY b/HISTORY new file mode 100644 index 0000000..27e3f31 --- /dev/null +++ b/HISTORY @@ -0,0 +1,217 @@ +VDR Plugin 'image' Revision History +----------------------------------- + +2005-01-09 Version 0.2.3 +- Bounce to vdr-1.3.18 +- Add -fPIC to CXXFLAGS (suggest by Thomas Schmidt) +- plugin: add reload image after commands execution, if image changed + +2004-12-06 +- Fix missing environ variable for examples/imagecmds.conf (Reported by Thomas Günther) +- Fix wrong folder flags on distribute archives (Reported by Thomas Günther) + +2004-12-05 +- Use now threadsafe functions replacement of strerror + +2004-12-02 +- Add missing -lz for static build with ffmpeg-0.4.9-pre1 + +2004-11-03 Version 0.2.2a +- bugfix release + +2004-10-18 +- plugin: include support for ffmpeg-0.4.9-pre1 + +2004-09-03 +- plugin: enlarge encode memory-areas, avoid encode failing (Reported by Christan Jacobsen) + +2004-09-01 Version 0.2.2 +- plugin: remove unused code on BuildPesPacket, avoid SIGSEGV on some cases (Reported by Tobias Grimm) + +2004-08-29 +- plugin: add header and format description on imagesources.conf/imagecmds.conf + +2004-08-24 +- plugin: change internal handling of delay for slideshow +- plugin: add DeviceFreeze/DevicePlay control, for better image change + +2004-08-22 Version 0.2.1 +- plugin: fix typo on housekeeping tmp-files + +2004-08-21 +- plugin: Updated finnish translations. (Thanks to Rolf Ahrenberg) +- plugin: include DVBDIR on libout/Makefile (Suggest by Rolf Ahrenberg) +- script: enable PATH by default (Suggest by Ronny Kornexl) + +2004-08-19 Version 0.2.0 +- plugin: check temporary file and try it to remove if it also existed. +- plugin: call of OSD_ErrorMsg only from OSD thread +- plugin: add new default housekeeping mode + ( use temporary filenames and remove this files at exit, + for use image_pregen.sh your must disable housekeeping at setup ) +- plugin: rewrite zoom +- plugin: remove option "Playback mode 'use DeviceStillPicture'" +- script: now error messages dumped to syslog, (/var/log/messages) +- Move functions to encode MPEG Frame from Script to Pluginssource + ( now plugin only depends package libavcodec1-dev to compile, + and package netpbm and libavcodec1 to run ) +- script: full rewrite to simple single script which only depends anytopnm / pnmscale, pnmfile, pnmcut, pnmflip + convert.sh/convert_jump.sh/convert_zoom.sh merged to one file, called imageplugin.sh +- plugin: rewrite generation of indexview +- plugin: use xpm-files for numbers as overlay and errorimage +- plugin: read ppm/pbm/pgm Files direct (see libimage) +- plugin: encode images with ffmpeg to playbackloop inside plugin (see liboutput) + +2004-07-31 Version 0.1.1 +- new release + +2004-07-30 +- plugin: add parser for placeholder %s on imagecmds.conf +- load imagecmds.conf now on menu open +- Exif State Viewer (see examples/imagecmds.conf with jpegtopnm -dumpexif %s > /dev/null ... ) + +2004-07-27 +- plugin: remove blocking umount, (thanks to Peter Juszack) + reorder slideshow management, outside player-image.c + +2004-07-26 +- scripts: change mkdir -v -p to mkdir -p, for work with busybox +- convert.sh: fix mkdir order before first use this folder + +2004-07-17 +- convert_jump.sh: Show if used, number on indexview inside image + +2004-07-07 +- scripts: prevent some zerosize files, e.g. vdr-image.pnm if pbmmake fail + +2004-07-03 Version 0.1.0 +- new release + +2004-07-01 +- some script bugfixes (thanks to Ronny Kornexl) + +2004-06-28 +- fix slow browser starting +- new faster threadmode, without polling on ProcessKey + + prepared for housekeeping and image pre-generation +- remove blocking key on converting, for commadostacking +- remove anymore necessary parts for create black.mpg, e.g. uuencode, gunzip + +2004-06-27 +- Implement a precompiled image, which show on detected error +- Include a used sample, other images possible with edit Makefile and "make errorimage" + + artwork be inspired on two icons from Nuvola vector icon theme which created by David Vignoni +- Plugin use now the real generated file, image.mpg is now out of use +- Little Scripts speedup +-Remove the last script step copy xxx.mpg to image.mpg, nocopy is now default + + update: contrib/image_pregen.sh + + update: all scripts/convert.sh,convert_functions.sh,convert_jump.sh,convert_zoom.sh +-Remove legacy around ImageMagick, to speedup scripts + + update: all scripts/convert.sh,convert_functions.sh,convert_jump.sh,convert_zoom.sh + +2004-06-20 Version 0.0.9 +- Redesign OSD for vdr-1.3.7+, thats work with skins + Hide Setup for ShowDate, therefore is'nt work anymore for vdr-1.3.7+ +- Change text around progressbar on part vdr-1.3.7+ + +2004-06-08 +- Added finnish translations. (Thanks to Rolf Ahrenberg) + +2004-06-06 +- Fix my mistake on zoom, (thanks to Thosten Schnebeck) + +2004-06-02 Version 0.0.9-rc2 +- Fix index view + +2004-06-02 Version 0.0.9-rc1 +- Fix plugin hangs on reassign files from browser, thanks to reportings this +- Added playback live audio from primary device, since VDR-1.3.8 support this + +2004-06-01 Version 0.0.9-pre6 +- Fix unquoted strings on scripts, thanks to reportings this +- Added check inside scripts for requirement external files + +2004-05-16 Version 0.0.9-pre5 +- update to VDR 1.3.7 + +2004-04-01 +- protect plugin before hang vdr on DeviceStillImage, if image.mpg has zero size +- extend error messages +- control environent from Plugin, to set ASPECT_RATIO,SHOW_NUMBERS,CONVERT_TEMPDIR + +2004-03-28: Verison 0.0.9-pre1 +- integrated scripts from Andreas Holzhammer, now no more ImageMagick !!!!!!!!!! +- changed zoom code, to start at 768x576 and Zoom level 1x +- fixup on some small issues in the scripts +- example scripts handling changed + +2004-03-27: +- make statusmessage for graphlcd-plugin recognizable, with prefix [image] + +2004-03-17: +- handle more error's +- add menu with commandos for images (userdefined imagecmd.conf) + +2004-03-14: +- changed if single file selected, + generate a slideshow for this folder start at selected file +- cleanup OSD/RC-Layouts for more usability + +2004-03-13: Verison 0.0.8 +- fixed to install.sh +- online readme is now the readme +- made history avail online +- readme and faq updated +- removed debian stuff, debian port now maintained by Tobias Grimm, see donwload area for more info + +2004-03-04: Version 0.0.8-rc2 +- changed background file, now generated using netpbm tools and placed in the cache folder +- enhanced slideshow time to max. 300 sec., no need to limit to 60 sec.... + +2004-03.03: Version 0.0.8-rc-1 +- Fix for commandline parsing (thx to Hollymolly and Henning from vdr-portal.de) +- README now has a DOUBLE NOTE if installing tools from source, use "./configure --prefix=/usr" ! + +2004-02-23: Version 0.0.8-pre-5 +- Added other File-Filterimplemation with more feature see examples/imagesources.conf.example + +2004-02-22: Version 0.0.8-pre-4 (onno ) +- Reworked convert* scripts for better aspect scaling +- Started to optionally replace ImageMagick by netpbm tools, check README.SCRIPTS +- Cleanup obsolete code from scripts +- Added image_pregen.sh to pre generate a file or folder (cron/at/..) + + +2004-02-22: Version 0.0.8-pre-3 + +- Add Filter for filetype (by Onno Kreuzinger) +- Implement Interface to Statusmonitor (this use e.g. GLCD-Plugin) +- Fix "Error: unknow config paramater ..." +- Make the most text translatable +- Change setupmenu form Integer 0/1 to yes/no Selection +- Remove some old file form mp3-plugins, and reformated the source with indent +- Fix Header with correct copyright +- Cleanup errorlog (esyslog) +- Remove some possible bufferoverflows throw sprint() + +2004-02-20: all versions between 0.0.5a and 0.0.8-pre are by Interpohl@vdr-portal.de + +Updated by Interpohl + + Extensions in V0.0.6 + - Autorepeat => default 0 => setup-parameter for endless loop of pictureshow + - remotelayouts => default 0 => setup-parameter for switching remote layout (see below) + - Using of function DeviceStillPicture or function send_pes_packet + => default 1 => setup-parameter + => DeviceStillPicture seems to be not works in VDR1.2.2(-6ct) + - Implementation of jumps (like as MP3- bzw. MPlayer-Plugin) + - Implementation of zooms (1..10) with possibility of navigation + + V0.0.6_1 + + Reworks on Jumpmenu (red button) + Now you get a 3x3 Matrix to navigate + +2003-01-31: Version 0.0.4 First Version + +- Initial revision. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..24b86d4 --- /dev/null +++ b/Makefile @@ -0,0 +1,130 @@ +# +# Makefile for a Video Disk Recorder plugin +# +# $Id$ + +# The official name of this plugin. +# This name will be used in the '-P...' option of VDR to load the plugin. +# By default the main source file also carries this name. +# +PLUGIN = image + +### The version number of this plugin (taken from the main source file): + +VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') + +### The C++ compiler and options: + +CXX ?= g++ +CXXFLAGS ?= -fPIC -O2 -Wall -Woverloaded-virtual + +### The directory environment: + +DVBDIR = ../../../../DVB +VDRDIR = ../../.. +LIBDIR = ../../lib +TMPDIR = /tmp +FFMDIR = ../../../../ffmpeg + +### Allow user defined options to overwrite defaults: + +-include $(VDRDIR)/Make.config + +### The version number of VDR (taken from VDR's "config.h"): + +VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g') + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### Includes and Defines (add further entries here): + +INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include -I. +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +DEFINES += -D_GNU_SOURCE + + +LIBS += liboutput/liboutput.a libimage/libimage.a + +ifdef FFMPEG_STATIC + INCLUDES += -I$(FFMDIR) + DEFINES += -DHAVE_FFMPEG_STATIC + LIBS += -L$(FFMDIR)/libavcodec -lavcodec -lz +else + LIBS += -ldl -rdynamic +endif + +### The object files (add further files here): + +OBJS = ${PLUGIN}.o +OBJS += i18n.o +OBJS += data.o +OBJS += menu.o +OBJS += data-image.o +OBJS += menu-image.o +OBJS += setup-image.o +OBJS += player-image.o +OBJS += control-image.o +OBJS += commands.o +OBJS += menu-commands.o +OBJS += list.o + +### The subdirectories: + +SUBDIRS = liboutput libimage + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< + +# Dependencies: + +MAKEDEP = $(CXX) -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Targets: + +all: subdirs libvdr-$(PLUGIN).so + +libvdr-$(PLUGIN).so: $(OBJS) + $(CXX) $(CXXFLAGS) -shared -export-dynamic $(OBJS) $(LIBS) -o $@ + @cp $@ $(LIBDIR)/$@.$(VDRVERSION) + + +subdirs: + @for i in $(SUBDIRS) ;\ + do \ + ( cd $$i;\ + $(MAKE) all;\ + ) \ + || exit 1;\ + done + +dist: clean + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @mkdir $(TMPDIR)/$(ARCHIVE) + @cp -a * $(TMPDIR)/$(ARCHIVE) + @chmod 644 -R $(TMPDIR)/$(ARCHIVE)/* + @find $(TMPDIR)/$(ARCHIVE) -name "*.sh" -exec chmod 755 {} \; + @find $(TMPDIR)/$(ARCHIVE) -type d -exec chmod 755 {} \; + @chown root.root -R $(TMPDIR)/$(ARCHIVE)/* + @tar czf $(PACKAGE).tar.gz -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(PACKAGE).tar.gz + +clean: + @-rm -f $(OBJS) $(DEPFILE) *.so *.tar.gz core* *~ contrib/*~ examples/*~ scripts/*~ + @for i in $(SUBDIRS) ;\ + do \ + ( cd $$i;\ + $(MAKE) clean;\ + ) \ + || exit 1;\ + done diff --git a/README b/README new file mode 100644 index 0000000..eedeced --- /dev/null +++ b/README @@ -0,0 +1,148 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Originaly written by: Kai Tobias Burwieck + "Interpohl" + Onno Kreuzinger + Andreas Brachold + +Former project's homepage: http://www.burwieck.net/vdr (dead?) + http://vdr-image.kreuzinger.biz> + +Maintainer: Andreas Brachold + +Project homepage: http://deltab.de/vdr/image.html + +Some files are from the mp3/MPlayer Plugin. Thanks to Stefan Hülswitt + + +Required: +----------- +- fullfeatured dvb card +- vdr 1.2.6+, series vdr 1.3.0 work too +- to compile plugin depends package libavcodec1-dev (tested with ffmpeg-0.4.8) +- to run your will need package netpbm and libavcodec1* +- netpbm 10.0+ (check for "anytopnm", if it does not exist upgrade/install netpbm) +- not necessary but useful utils/file (file) for better imagetyp detection, used by anytopnm + +*) it's use libavcodec.so, also static linking possible with compile #> make plugin FFMPEG_STATIC=1 + +Important Note: +if tools are installed by source absolutely also for it provide that the +programs (anytopnm pnmscale pnmfile pnmcut pnmflip) are inside the search-path, +otherwise adapt the variable "PATH" within the file imageplugin.sh. + +Install: +------------ +Install the plugin part as usual (see vdr docs if you not know already). + +Compile depends package libavcodec1-dev, libavcodec1 or also know ffmpeg. +later for run your will only need libavcodec1(libavcodec.so) and netpbm. +("apt-get install libavcodec1-dev libavcodec1 netpbm" on Debian) + +In the ./examples directory you can find a sample file for +imagesources.conf, change this file if needed and place it in +the plugins folder in your config folder (the video folder by default). + +The syntax is : +;;<0/1>; + +examples: +/media/cdrom;CDROM;1;*.jpg *.jpeg *.png *.tif* *.bmp +/archive/photos/holiday;Holiday pictures;0;DSC10*.jpg + +0/1 is for media that need to be mounted (uses the same mount.sh +as mplayer/mp3 plugin). + + +The file ./scripts/imageplugin.sh could go to /usr/bin, or any other folder in your path. +You can also use the plugin options (vdr -help after installing the plugin) to specify +the path's to the files, if you don't want to those files in you system. +[ e.g.: ./vdr '-Pimage -C /path/imageplugin.sh -m /path/mount.sh' ...] +The install.sh in the examples folder will try a automatic install, give it a try if it fails else. + +Manual: +------------ +Start the plugin, than select a file (or folder) and press OK. + + +At all modi + OK toggle OSD informations + Play/Pause begin/halt slide + Stop/Blue stop plugin + Red open menu with imagecommands + +While watching pictures + Back stop plugin + + Left previous picture + Right next picture + Down jump three pictures back + Up jump three pictures forward + + 7 jump five pictures back + 9 jump five pictures forward + + 0 view original image + 1 Rotate 90 Grad counter clockwise + 3 Rotate 90 Grad clockwise direction + + 4 slide show - decrease the time (seconds) each picture shows + 6 slide show - increase the time (seconds) each picture shows + 5 Zoom inside image + 8 Zoom outside image and call Jumpmenu + +'Zoom menu' + Back Zoom outside image + + Left scroll zoomedframe inside picture to left + Right scroll zoomedframe inside picture to right + Down scroll zoomedframe inside picture to left + Up scroll zoomedframe inside picture to up + + 0 view original image + 5 Zoom inside image + 8 Zoom outside image + +'Jump menu' (3x3 preview): + Back view original image + + Left previous picture group (go 9 pictures back) + Right next picture group (go to the next 9 pictures) + + 0 view original image + 1 ... 9 pick the corresponding image + +(thx for the english readme/keymapping description to Kai Steinbach) + +image commands +------------ +The file imagecmds.conf can be used to define commands that can be applied to the +currently viewed image. The syntax is exactly the same as described for the +file commands.conf, see "man 5 vdr". If only one instruction is indicated, +the filename of the image will be appended to the command string, +separated by a blank and enclosed in single quotes. + +Format> Menutext ?: command %s +? - Confirmation request (optionally) +%s - placeholder for filename (optionally) + +See for a sample at examples/imagecmds.conf . + + +Diagnosis of the scripts and installation +----------------------------------------- +If the script does not work like it should, start the Script to test from the console. + +imageplugin.sh [infile] [outfile] [WIDTH] [HEIGHT] [ZOOMFACTOR] [LEFTPOS] [TOPPOS] {FLIPCMD} +e.g. imageplugin.sh myimage.png outfile.pnm 720 576 0 0 0 original + +and please examine the produced syslog messages at /var/log/message or messages on console + +if that does not help repeats the procedure with > export DEBUG=yes; + +export DEBUG=yes;imageplugin.sh myimage.png outfile.pnm 720 576 0 0 0 original + +and examine the expenditure on the screendump + +*) if script work for your, it's could reduce the message on syslog, +if your change on imageplugin.sh inside from VERBOSE=yes to VERBOSE=no diff --git a/README.DE b/README.DE new file mode 100644 index 0000000..39600b5 --- /dev/null +++ b/README.DE @@ -0,0 +1,152 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Originaly written by: Kai Tobias Burwieck + "Interpohl" + Onno Kreuzinger + Andreas Brachold + +Former project's homepage: http://www.burwieck.net/vdr (dead?) + http://vdr-image.kreuzinger.biz> + +Maintainer: Andreas Brachold + +Project homepage: http://deltab.de/vdr/image.html + + +Einige Dateien stammen vom mp3/MPlayer Plugin, der danke geht an Stefan Hülswitt + + + +Benötigt: +----------- +- Full-Featured DVB Karte +- vdr 1.2.6+ +- das Plugin ist abhängig vom Paket libavcodec1-dev zu übersetzen, (getestet mit ffmpeg-0.4.8) +- und Paket netpbm sowie libavcodec1* zur Ausführung +- netpbm 10.0+ (prüfe ob das Tool "anytopnm" existiert) +- nicht notwendig, aber sinnvoll utils/file (file) für bessere Bildtyperkennung, mit anytopnm + +*) es wird libavcodec.so nachgeladen, aber auch statisches Linken ist mit möglich mit Kompilierung per #> make plugin FFMPEG_STATIC=1 + +WICHTIG, +wenn Tools von Source installiert werden, unbedingt auch dafür sorgen das +die Programme (anytopnm pnmscale pnmfile pnmcut pnmflip) innerhalb des Such-Pfade liegen, +ansonsten die Variable "PATH" innerhalb der Datei imageplugin.sh anpassen. + +Installation: +------------------ +Das plugin wird wie alle plugins installiert (siehe vdr Dokumentation). + +Das Kompilieren ist abhängig vom Paket libavcodec1-dev, libavcodec1, aber auch bekannt als ffmpeg. +Später zum Ausführen wird nur noch libavcodec1(libavcodec.so) und "netpbm" benötigt. +("apt-get install libavcodec1-dev libavcodec1 netpbm" in Debian) + + +Im ./examples Ordner liegt eine Beispiel imagesources.conf, dies ggf. +anpassen und in das plugins Verzeichnis des config Ordners legen. + +Die Syntax der Datei ist: +;;<0/1>; + +Beispiele: +/media/cdrom;CDROM;1;*.jpg *.jpeg *.png *.tif* *.bmp +/archive/photos/holiday;Urlaubs Bilder;0;DSC10*.jpg + +0/1 ist für Quellen die gemountet werden müssen (benutzt das gleiche mount.sh wie +das mplayer/mp3 plugin). +gefolgt mit einer Liste des Suchmuster für Dateien welche angezeigt werden sollen, +welche dann jeweils mit Leerzeichen getrennt werden. + +Die Datei imageplugin.sh sollte nach /usr/bin kopiert werden, bzw. in einen anderen +gemeinsamen Ordner innerhalb Such-Path kopieren werden, +alternativ können als Kommandozeilenoption die gewählten Pfade zu dem Skript +beim vdr Start übergeben werden. +[z.B.: ./vdr '-Pimage -C /pfad/imageplugin.sh -m /pfad/mount.sh' ...] + + +Bedienung: +----------------- +Nach dem Aufrufen entweder eine Datei oder einen Ordner auswählen und mit OK wählen. + +At all modi + OK toggle OSD informations + Play/Pause Starte Diaschau/ Stoppe Diaschau + Stop/Blue Stoppe Plugin + Red Öffne Menu mit Bildbefehle + +Während der Bildbetrachtung: + Back Stoppe Plugin + + Left vorheriges Bild + Right nächstes Bild + Down Springe drei Bilder zurück + Up Springe drei Bilder vorwärts + + 7 Springe fünf Bilder zurück + 9 Springe fünf Bilder vorwärts + + 0 Zeige Originalbild + 1 Rotatiere 90 Grad gegen den Uhrzeigersinn + 3 Rotatiere 90 Grad im Uhrzeigersinn + + 4 Diaschau-WeiterschaltwertinSekundenverkleinern + 6 Diaschau-WeiterschaltwertinSekundenerhöhen + + 5 "Zoomwert erhöhen" und damit Zoom-Bedienung starten + 8 "Zoomwert verkleinern" und damit Jump-Bedienung starten + +'Zoom-Bedienung' + Back Zoomwert verkleinern + + Left Bildausschnitt nach Links verschieben + Right Bildausschnitt nach Rechts verschieben + Down Bildausschnitt nach Unten verschieben + Up Bildausschnitt nach Oben verschieben + + 0 view original image + 5 Zoomwert erhöhen + 8 Zoomwert verkleinern + +Jump-Menu (3x3 Vorschau): + Back Zeige Originalbild + + Left vorherige Bildgruppe (9 Bilder weiter in der Vorschau) + Right nächste Bildgruppe (9 Bilder vorher in der Vorschau) + + 0 Zeige Originalbild + 1 ... 9 Wählt ensprechendes Bild + +Bildbefehle +------------ +Die Datei imagecmds.conf kann dazu genutzt werden, um definierbare Befehle auf +auf das an gezeigte Bild anzuwenden. Der Syntax ist extakt der gleiche der +für die Datei commands.conf unter "man 5 vdr" beschrieben wurde. +Wenn nur ein Befehl angegeben wird, wird der Datei des Bildes angehängt an den Befehlstext, +getrennt durch ein Leerzeichen und einschlossen in Anführungszeichen (single quotes). + +Format> Menutext ?: Kommando %s +? - Bestätigung anfordern, (optional) +%s - Platzhalter für Dateinamen (optional) + +Schaue für ein Beispiel nach examples/imagecmds.conf.DE . + + +Diagnose des Script +------------------- +Wenn der Script nicht arbeitet wie er sollten, starte den Script zum Testen von der Konsole. + +imageplugin.sh [infile] [outfile] [WIDTH] [HEIGHT] [ZOOMFACTOR] [LEFTPOS] [TOPPOS] {FLIPCMD} +z.B. imageplugin.sh myimage.png outfile.pnm 720 576 0 0 0 original + +und bitte überprüfe die erzeugten Nachrichten im Syslog in /var/log/message bzw. +die Ausgaben auf dem Bildschirm + + +wenn das nicht hilft wiederhole die Prozedur mit > export DEBUG=yes; + +export DEBUG=yes;imageplugin.sh myimage.png outfile.pnm 720 576 0 0 0 original + +und überprüfe die Ausgabe auf dem Bildschirm. + +*) Wenn der Script für dich arbeitet, es können die Ausgaben im Syslog reduziert werden, +wenn Du innerhalb von imageplugin.sh die Zeile von VERBOSE=yes zu VERBOSE=no geändert wird. diff --git a/TODO b/TODO new file mode 100644 index 0000000..72fcea4 --- /dev/null +++ b/TODO @@ -0,0 +1,7 @@ +TODO + +Proper Scale on 16:9 +Exif automated rotation + +Change BLUE/EXIT > Stop to BLUE > Stop / EXIT > reshow Browser +reload Image after commands execution, if image changed diff --git a/commands.c b/commands.c new file mode 100644 index 0000000..f1649ea --- /dev/null +++ b/commands.c @@ -0,0 +1,239 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include + +#include "commands.h" +#include "setup-image.h" + + +// --- cImageCommand ------------------------------------------------------------- + +char *cImageCommand::m_szLastResult = NULL; + +cImageCommand::cImageCommand(void) +: m_szTitle(NULL) +, m_szCommand(NULL) +, m_bConfirm(false) +{ + +} + +cImageCommand::~cImageCommand() +{ + if(m_szTitle) + { + free(m_szTitle); + m_szTitle = NULL; + } + if(m_szCommand) + { + free(m_szCommand); + m_szCommand = NULL; + } +} + +bool cImageCommand::Parse(const char *s) +{ + const char *p = strchr(s, ':'); + if(p) + { + int l = p - s; + if(l > 0) + { + m_szTitle = MALLOC(char, l + 1); + stripspace(strn0cpy(m_szTitle, s, l + 1)); + if(!isempty(m_szTitle)) + { + int l = strlen(m_szTitle); + if(l > 1 && m_szTitle[l - 1] == '?') + { + m_bConfirm = true; + m_szTitle[l - 1] = 0; + } + m_szCommand = stripspace(strdup(skipspace(p + 1))); + return !isempty(m_szCommand); + } + } + } + return false; +} + + + +const char *cImageCommand::Execute(const char *szFileName) +{ + char *szCmdBuf = NULL; + + if(m_szLastResult) + { + free(m_szLastResult); + m_szLastResult = NULL; + } + + // Combine command and filename + if(szFileName && m_szCommand) { + + if(NULL == strstr(m_szCommand, "%s")) { + /// Merge command and filename e.g : identify 'my_image.png' + asprintf(&szCmdBuf, "%s \'%s\'", m_szCommand, szFileName); + } + else { + /// Replace Mode, replace any %s with filename + /// e.g. : jpegtran -rotate 90 %s > $CONVERT_TEMPDIR/tmp.jpg && mv $CONVERT_TEMPDIR/tmp.jpg %s + /// would to jpegtran -rotate 90 'my_image.png' > $CONVERT_TEMPDIR/tmp.jpg && mv $CONVERT_TEMPDIR/tmp.jpg 'my_image.png' + char *szF; + asprintf(&szF, "\'%s\'", szFileName); + if(szF) { + szCmdBuf = (char*)calloc(PATH_MAX,1); + if(szCmdBuf) + { + char* d = szCmdBuf; + char* s = m_szCommand; + for(;d-szCmdBuf < PATH_MAX-1 && *s != '\0';++s,++d) + { + if(*s == '%' && *(s+1) == 's') + { + strncat(szCmdBuf,szF,PATH_MAX-1); + d += strlen(szF)-1; + ++s; + } + else + *d = *s; + } + } + } + free(szF); + } + } + + const char *szCmd = szCmdBuf ? szCmdBuf : m_szCommand; + dsyslog("imageplugin: executing command '%s'", szCmd); + + // Set environment + ImageSetup.SetEnv(); + + FILE *p = popen(szCmd, "r"); + if(p) + { + int l = 0; + int c; + while((c = fgetc(p)) != EOF) + { + if(l % 20 == 0) { + m_szLastResult = (char *)realloc(m_szLastResult, l + 21); + if(!m_szLastResult) + break; + } + m_szLastResult[l++] = c; + } + if(m_szLastResult) + m_szLastResult[l] = 0; + pclose(p); + } + else + esyslog("imageplugin: can't open pipe for command '%s'", szCmd); + + if(szCmdBuf) + free(szCmdBuf); + + return m_szLastResult; +} + +cImageCommands::cImageCommands(void) +: m_szFileName(NULL) +{ + +} + +cImageCommands::~ cImageCommands() +{ + Clear(); +} + +void cImageCommands::Clear(void) +{ + if(m_szFileName) + free(m_szFileName); + m_szFileName = NULL; + cList < cImageCommand >::Clear(); +} + + +bool cImageCommands::Load(const char *szFileName/* = NULL*/, bool bAllowComments /*=true*/, bool bMustExist /*= false*/) +{ + Clear(); + if(szFileName) + { + m_szFileName = strdup(szFileName); + m_bAllowComments = bAllowComments; + } + + bool bRet = !bMustExist; + if(m_szFileName && access(m_szFileName, F_OK) == 0) + { + isyslog("imageplugin: loading %s", m_szFileName); + FILE *f = fopen(m_szFileName, "r"); + if(f) + { + int n = 0; + char szBuf[8192]; + bRet = true; + while(fgets(szBuf, sizeof(szBuf), f) > 0) + { + ++n; + if(m_bAllowComments) + { + char *p = strchr(szBuf, '#'); + if(p) + *p = 0; + } + stripspace(szBuf); + if(!isempty(szBuf)) + { + cImageCommand *l = new cImageCommand; + if(l->Parse(szBuf)) + { + Add(l); + } + else + { + esyslog("imageplugin: error in %s, line %d\n",m_szFileName, n); + delete l; + bRet = false; + break; + } + } + } + fclose(f); + } else + { + esyslog("imageplugin: error %s: %m", m_szFileName); + bRet = false; + } + } + if(!bRet) + esyslog("imageplugin: error while reading '%s'\n", m_szFileName); + return bRet; +} diff --git a/commands.h b/commands.h new file mode 100644 index 0000000..39f7646 --- /dev/null +++ b/commands.h @@ -0,0 +1,64 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +// --- cImageCommands --------------------------------------------------------- + +#ifndef __COMMANDS_H +#define __COMMANDS_H + +#include + +class cImageCommand +: public cListObject +{ + char *m_szTitle; + char *m_szCommand; + bool m_bConfirm; + static char *m_szLastResult; +public: + cImageCommand(void); + virtual ~ cImageCommand(); + + bool Parse(const char *s); + + const char *Title(void) const { return m_szTitle; } + bool Confirm(void) const { return m_bConfirm; } + const char *Command(void) const{ return m_szCommand;} + + const char *Execute(const char *szFileName = NULL); +}; + +class cImageCommands +: public cList < cImageCommand > +{ + char *m_szFileName; + bool m_bAllowComments; +protected: + void Clear(void); +public: + cImageCommands(void); + virtual ~ cImageCommands(); + + const char *FileName(void) const { return m_szFileName; } + bool Load(const char *szFileName = NULL, bool bAllowComments = true, bool bMustExist = false); +}; + +#endif //__COMMANDS_H diff --git a/contrib/image_pregen.sh b/contrib/image_pregen.sh new file mode 100755 index 0000000..8a89fd6 --- /dev/null +++ b/contrib/image_pregen.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# image_pregen.sh +# +# call with file or dir to pregenerate the file or dir +# if you set first param. to -at, the script will use at to start a job for +# atd, so it runs asychonous in the background +# +# by Onno Kreuzinger, onno_AT_kreuzinger_DOT_biz +# 2004-06-27 a.brachold - remove nocopy method +# 2004-08-14 a.brachold - update for imageplugin.sh version 0.2.0 +# +[ "z$DEBUG" = "zyes" ] && set -xv + +[ "z$1" = "z" ] && echo "start atleast with file or dir as argument, to pregenerate that file or dir." && exit 1 +[ "z$1" = "z-at" ] && echo "starting at job mode.." && echo "$0 $2 $3"|at now && exit 0 +[ "z$2" = "z-at" ] && echo "starting at job mode.." && echo "$0 $1 $3"|at now && exit 0 +[ "z$1" = "z-follow" ] && FOLLOWMODE="true" && shift +[ "z$2" != "z" ] && shift + +CONVERT_SCRIPT="imageplugin.sh" +CACHEFOLDER=${CACHEFOLDER:-"/tmp/image"} + +[ ! -e $CONVERT_SCRIPT ] && echo "Can't find our worker $CONVERT_SCRIPT, please adjust path or variable CONVERT_SCRIPT" && exit 1 + +if [ -d "$1" ] + then + if [ "z$FOLLOWMODE" = "ztrue" ] + then + echo "$0 : recursive dir mode "$1"" + find "$1" -follow -type f \ + -name "*\.[jJbBtTpPmM][pPmMiInN][gGpPeEfFmM]" \ + -exec $CONVERT_SCRIPT "{}" "$CACHEFOLDER{}.pnm" 688 544 0 0 0 \; 2> /dev/null + else + echo "$0 : dir mode "$1"" + find "$1" -maxdepth 1 -follow -type f \ + -name "*\.[jJbBtTpPmM][pPmMiInN][gGpPeEfFmM]" \ + -exec $CONVERT_SCRIPT "{}" "$CACHEFOLDER{}.pnm" 688 544 0 0 0 \; 2> /dev/null + fi +elif [ -e "$1" ] + then + echo "$0 : single file mode "$1"" + $CONVERT_SCRIPT "$1" "$CACHEFOLDER$1.pnm" 688 544 0 0 0 2> /dev/null +else + echo -e "$1 is no file or directory\n syntax: $0 [-at] [-follow] " +fi +echo "done" diff --git a/contrib/install-scripts.sh b/contrib/install-scripts.sh new file mode 100755 index 0000000..8f2d34d --- /dev/null +++ b/contrib/install-scripts.sh @@ -0,0 +1,35 @@ +#!/bin/sh +#set -xv +pushd $(dirname $0) &>/dev/null + +BIN_FILES="../scripts/imageplugin.sh ../contrib/image_pregen.sh" +BIN_DEST="/usr/bin" + +echo -e "Warning: this script will install these files:\n$BIN_FILES\nto $BIN_DEST,\npress CTRL-C to abort, or anykey to begin." +read ANYKEY + +#installing bin files +echo "Installing $BIN_FILES to $BIN_DEST....." +for FILE in $BIN_FILES +do + echo "copying $FILE to $BIN_DEST ..." + rm -f $BIN_DEST/$FILE + cp -f $FILE $BIN_DEST/$FILE;RV=$? + [ $RV -ne 0 ] && echo -e "Errorcode $RV on file: $BIN_DEST/$FILE.\n" +done + +#check and install mount.sh +if $(which mount.sh &>/dev/null) + then + OLD_MOUNT=$(which mount.sh) + echo -e "Should the existing mount.sh ("$OLD_MOUNT")\nbe overwriten? (y/N)" + read -n 1 -t 90 $KEYPRESSED + [ "z$KEYPRESSED" = "zy" -o "z$KEYPRESSED" = "zY" ] && rm -f $OLD_MOUNT && cp -f ../scripts/mount.sh $OLD_MOUNT + else + cp ../scripts/mount.sh $BIN_DEST/mount.sh +fi + +which mount.sh &>/dev/null || echo -e "Error could not execute mount.sh, please check.\n" + +popd &>/dev/null &>/dev/null +echo -e "Remember to copy/create imagesources.conf and imagecmds.conf\nfiles in your plugins config directory." diff --git a/control-image.c b/control-image.c new file mode 100644 index 0000000..b109c50 --- /dev/null +++ b/control-image.c @@ -0,0 +1,1137 @@ +/* +* Image plugin to VDR (C++) +* +* (C) 2004 Andreas Brachold +* based on (C) 2003 Kai Tobias Burwieck +* +* This code 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 2 +* of the License, or (at your option) any later version. +* +* This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* Or, point your browser to http://www.gnu.org/copyleft/gpl.html +*/ + +#include + +#include "player-image.h" +#include "control-image.h" +#include "data-image.h" +#include "setup-image.h" +#include "i18n.h" +#include "menu-commands.h" +#include "list.h" + +#include +#include +#include + + +#if VDRVERSNUM < 10307 +// --- cProgressBar ------------------------------------------------------------ +class cProgressBar:public cBitmap { + public: + cProgressBar(int nWidth, int nHeight, int nProgressBegin, int nProgressEnd, int nProgressRange) + :cBitmap(nWidth, nHeight,2) + { + if(nProgressRange > 0) + { + if(nProgressBegin>0) + { + int b = nProgressBegin * width / nProgressRange; + int p = nProgressEnd * width / nProgressRange; + Fill(0, 0, b, nHeight - 1, clrWhite); + Fill(b + 1, 0, p, nHeight - 1, clrGreen); + Fill(p + 1, 0, nHeight - 1, nHeight - 1, clrWhite); + } + else + { + int p = nProgressEnd * width / nProgressRange; + Fill(0, 0, p, nHeight - 1, clrGreen); + Fill(p + 1, 0, nWidth - 1, nHeight - 1, clrWhite); + } + } + } +}; +#endif + + +// --- cImageControl --------------------------------------------------------- + +char* cImageControl::m_szLastShowStatusMsg = 0; + +void cImageControl::SetSlideShow(cSlideShow * pNewSlideShow) +{ + // Remove old session + if(cControl::Control()) + cControl::Shutdown(); + + if(pNewSlideShow) { + cImageControl* pControl = new cImageControl(pNewSlideShow); + cControl::Launch(pControl); + } +} +////////////////////////////////////////////////////////////////////////////// +/** C-tor Create for control-object to control the image-player +*/ +cImageControl::cImageControl(cSlideShow * pNewSlideShow) + : cControl(player = new cImagePlayer(pNewSlideShow)) + , m_pImageMenu(NULL) +#if VDRVERSNUM >= 10307 + , m_pDisplayReplay(NULL) +#endif +{ + // Notity all cStatusMonitor + cStatus::MsgReplaying(this, "[image]"); + + m_tStarted = time(NULL); + + m_eOSDStatusIsOpen = eDisplayNothing; + // Depends the Setup of ShowReplayMode is ModeOnly visable + m_eOSDStatusVisable = Setup.ShowReplayMode?eDisplayModeOnly:eDisplayNothing; + + m_ePlayMode = ePlayModeNormal; + m_bSlideShowActiv = ImageSetup.SlideShow; + m_bSlideShowBefore = false; + + // Support for Zoom + m_nZoomFactor = 0; + m_nRealImageWidth = 0; + m_nRealImageHeight = 0; +} + + +////////////////////////////////////////////////////////////////////////////// +/** D-tor Destroy control-object to control the image-player +*/ +cImageControl::~cImageControl() +{ + if(m_pImageMenu) + delete m_pImageMenu; + m_pImageMenu = NULL; + // Notity cleanup all cStatusMonitor + cStatus::MsgReplaying(this, NULL); + // Hide OSD + HideOSD(); +#if VDRVERSNUM >= 10307 + if(m_pDisplayReplay) { + delete m_pDisplayReplay; + m_pDisplayReplay = NULL; + } +#endif + // Stop Playback + Stop(); + if(player) + delete player; + player = NULL; + + if(m_szLastShowStatusMsg) + free(m_szLastShowStatusMsg); + m_szLastShowStatusMsg = 0; +} + +void cImageControl::Show(void) +{ + ShowOSD(); +} + +void cImageControl::Hide(void) +{ + m_eOSDStatusVisable = eDisplayNothing; + HideOSD(); + + if(m_szLastShowStatusMsg) + free(m_szLastShowStatusMsg); + m_szLastShowStatusMsg = 0; +} + +void cImageControl::ShowOSD() +{ + switch(m_eOSDStatusVisable) { + case eDisplayNothing: HideOSD(); break; + case eDisplayModeOnly: ShowMode(); break; + case eDisplayProgress: ShowProgress(); break; + } +} + +void cImageControl::HideOSD(void) +{ + if(eDisplayNothing != m_eOSDStatusIsOpen) { +#if VDRVERSNUM < 10307 + Interface->Close(); +#else + if(m_pDisplayReplay) { + delete m_pDisplayReplay; + m_pDisplayReplay = NULL; + } +#endif + m_eOSDStatusIsOpen = eDisplayNothing; + } + + if (m_pImageMenu) { + delete m_pImageMenu; + m_pImageMenu = NULL; + } +} +////////////////////////////////////////////////////////////////////////// +/** Send Message if changed to any statusmonitor +*/ +void cImageControl::ShowStatusMsg() +{ + char* sz = 0; + if(IsConvertRunning()) // Display that convert is running + { + asprintf(&sz,"[image] %s",tr("Convert...")); + } + else + { + switch(m_ePlayMode) + { + case ePlayModeNormal: asprintf(&sz,"[image] %s",FileName());break; + case ePlayModeJump: asprintf(&sz,"[image] %s",tr("Select picture via key 1..9!"));break; + case ePlayModeZoom: asprintf(&sz,"[image] %s %dx",tr("Zoom"),m_nZoomFactor);break; + } + } + if(sz) + { + if(!m_szLastShowStatusMsg + || 0 != strcmp(m_szLastShowStatusMsg,sz)) + { + if(m_szLastShowStatusMsg) + free(m_szLastShowStatusMsg); + m_szLastShowStatusMsg = sz; + cStatus::MsgReplaying(this, m_szLastShowStatusMsg); + } + else + free(sz); + } +} + +void cImageControl::ShowMode(void) +{ + if(eDisplayModeOnly != m_eOSDStatusIsOpen) + { +#if VDRVERSNUM < 10307 + Interface->Open(0, -1); +#else + m_pDisplayReplay = Skins.Current()->DisplayReplay(m_eOSDStatusVisable==eDisplayModeOnly); +#endif + m_eOSDStatusIsOpen = eDisplayModeOnly; + } + +#if VDRVERSNUM < 10307 + bool bFixFont = false; + int nMaxCharacter = Interface->Width(); //Get OSD-Display width +#else + int nMaxCharacter = m_pDisplayReplay->EditableWidth(); //FIXME Get OSD-Display width +#endif + + char *sz = MALLOC(char, nMaxCharacter + 1); + + if(IsConvertRunning()) // Display that convert is running + { + strn0cpy(sz, tr("Convert..."), nMaxCharacter); + } + else // else Display depends on current play the status + { + switch(m_ePlayMode) + { + // Display normal playstatus + default: + case ePlayModeNormal: + { +#if VDRVERSNUM < 10307 + if(m_bSlideShowActiv) + { strn0cpy(sz, " > ", nMaxCharacter); bFixFont = true; } + else + { strn0cpy(sz, " || ", nMaxCharacter); bFixFont = true; } +#else + // Get the current activ filename + const char* szFileName = FileName(); + if(!szFileName) + return; + snprintf(sz, nMaxCharacter, "%s", szFileName); +#endif + break; + } + // Display jumpmode playstatus + case ePlayModeJump: + { + strn0cpy(sz, tr("Select picture via key 1..9!"), nMaxCharacter); + break; + } + // Display zoommode playstatus + case ePlayModeZoom: + { + snprintf(sz, nMaxCharacter, "%s %dx",tr("Zoom"),m_nZoomFactor); + } + } + } + +#if VDRVERSNUM < 10307 + if(bFixFont) { + eDvbFont OldFont; + OldFont = Interface->SetFont(fontFix); + DisplayAtBottom(sz); + Interface->SetFont(OldFont); + } + else { + DisplayAtBottom(sz); + } +#else + m_pDisplayReplay->SetMode(m_ePlayMode == ePlayModeNormal && m_bSlideShowActiv, true, 1); + if(m_eOSDStatusVisable!=eDisplayModeOnly) + m_pDisplayReplay->SetTitle(sz); +#endif + free(sz); +} + +#if VDRVERSNUM < 10307 +void cImageControl::DisplayAtBottom(const char *s) +{ + const int p = (eDisplayModeOnly == m_eOSDStatusVisable) ? 0 : 2;// ???? Unused + if(s) { + const int d = std::max(Width() - cOsd::WidthInCells(s), 0) / 2; + if(eDisplayModeOnly == m_eOSDStatusVisable) + Interface->Fill(0, p, Interface->Width(), 1, clrTransparent); + Interface->Write(d, p, s); + } + else + Interface->Fill(12, p, Width() - 22, 1, clrBackground); +} +#endif + +void cImageControl::ShowProgress(void) +{ + // Get the current activ filename + const char* szFileName = FileName(); + if(!szFileName) + return; + + if(eDisplayProgress != m_eOSDStatusIsOpen) { + HideOSD(); +#if VDRVERSNUM < 10307 + Interface->Open(Setup.OSDwidth, -3); +#else + m_pDisplayReplay = Skins.Current()->DisplayReplay(m_eOSDStatusVisable==eDisplayModeOnly); + m_pDisplayReplay->SetMarks(&m_Marks); +#endif + m_eOSDStatusIsOpen = eDisplayProgress; + } + +#if VDRVERSNUM < 10307 + Interface->Clear(); + int nMaxCharacter = Interface->Width(); //Get OSD-Display width +#else + int nMaxCharacter = m_pDisplayReplay->EditableWidth(); //FIXME Get OSD-Display width +#endif + //******************************************************************** + // build first Line + char *sz = MALLOC(char, nMaxCharacter + 1); + + if(IsConvertRunning()) // Display that convert is running + { + snprintf(sz, nMaxCharacter, "%s", tr("Convert...")); + } + else + { +#if VDRVERSNUM < 10307 + int n = strlen(szFileName); + if(n > nMaxCharacter) { //Clip long filenames + n = n - Width() + 4; + if(n < 0) n = 0; + snprintf(sz, nMaxCharacter, "... %s", szFileName + n); + } + else { + snprintf(sz, nMaxCharacter, "%s", szFileName); + } +#else + switch(m_ePlayMode) { + case ePlayModeJump: + snprintf(sz, nMaxCharacter, "%s", + tr("Select picture via key 1..9!")); + break; + case ePlayModeZoom: + snprintf(sz, nMaxCharacter, "%s: %dx (%dx%d)",tr("Zoom"), + m_nZoomFactor, + m_nRealImageWidth * m_nZoomFactor, + m_nRealImageHeight * m_nZoomFactor); + break; + case ePlayModeNormal: + snprintf(sz, nMaxCharacter, "%s", szFileName); + break; + } +#endif + } + +#if VDRVERSNUM < 10307 + Interface->Write(0, 0, sz); +#else + m_pDisplayReplay->SetTitle(sz); +#endif + + //******************************************************************** + // show on second line the progressbar +#if VDRVERSNUM < 10307 + { + int nProgressBegin = 0; + int nProgressEnd = ImageCurrent(); + int nProgressRange = ImageTotal(); + + if(ePlayModeJump == m_ePlayMode) // Show on Jump mode only the selected Range + { + nProgressBegin = std::max(0, nProgressEnd - 1); + nProgressEnd = std::min(nProgressBegin + 9,nProgressRange); + } + + cProgressBar ProgressBar(Width() * cOsd::CellWidth(), + cOsd::LineHeight(), nProgressBegin, nProgressEnd, nProgressRange); + Interface->SetBitmap(0, cOsd::LineHeight(), ProgressBar); + } +#else + m_pDisplayReplay->SetProgress(ImageCurrent(), ImageTotal()); + + snprintf(sz, nMaxCharacter, "%3d", ImageCurrent()); + m_pDisplayReplay->SetCurrent(sz); + snprintf(sz, nMaxCharacter, "%3d", + m_ePlayMode==ePlayModeJump? + std::min(ImageCurrent()+9,ImageTotal()) + :ImageTotal()); + m_pDisplayReplay->SetTotal(sz); + // Remember to me : SetTotal/SetCurrent need clean Screen if changed stringformat +#endif + +#if VDRVERSNUM < 10307 + //******************************************************************** + // show on third line a more information + switch(m_ePlayMode) + { + case ePlayModeJump: + { + snprintf(sz, nMaxCharacter, "%s", + tr("Select picture via key 1..9!")); + // center the Message + const int d = std::max(Width() - cOsd::WidthInCells(sz),0) / 2; + Interface->Write(d, 2,sz); + break; + } + case ePlayModeZoom: + { + snprintf(sz, nMaxCharacter, "%s: %dx (%dx%d)",tr("Zoom"), + m_nZoomFactor, + m_nRealImageWidth * m_nZoomFactor, + m_nRealImageHeight * m_nZoomFactor); + // center the Message + const int d = std::max(Width() - cOsd::WidthInCells(sz),0) / 2; + Interface->Write(d, 2,sz); + break; + } + case ePlayModeNormal: + { + char szSlideShowInfo[32]; + szSlideShowInfo[0] = '\0'; + + if(m_bSlideShowActiv && !IsConvertRunning()) + { + int t = time(NULL) - m_tStarted + 1; + snprintf(szSlideShowInfo, sizeof(szSlideShowInfo), " | %d/%d", + t, ImageSetup.SSsec); + } + snprintf(sz,nMaxCharacter, "(%3d/%3d)%s", + ImageCurrent(), ImageTotal(), szSlideShowInfo); + Interface->Write(0, 2, sz); + + if(ImageSetup.ShowDate && !IsConvertRunning()) + { + struct stat stFile; + if(0 != stat(szFileName, &stFile)) { + char szErr[128]; + int nErr = errno; + szErr[sizeof(szErr)-1] = '\0'; + if(0 != strerror_r(nErr,szErr,sizeof(szErr)-1)) { + szErr[0] = '\0'; + } + esyslog("imageplugin: can't get filestate %s (%d)%s.\n",szFileName,nErr,szErr); + } + else + { + struct tm *timestr; + if((timestr = localtime(&(stFile.st_mtime)))) + { + snprintf(sz,nMaxCharacter, + "(%2.2d.%2.2d.%2.2d-%2.2d:%2.2d)", + (int)timestr->tm_mday, + (int)timestr->tm_mon + 1, + (int)timestr->tm_year % 100, + (int)timestr->tm_hour, + (int)timestr->tm_min); + + Interface->Write(nMaxCharacter - strlen(sz), 2, sz); + } + else { + char szErr[128]; + int nErr = errno; + szErr[sizeof(szErr)-1] = '\0'; + if(0 != strerror_r(nErr,szErr,sizeof(szErr)-1)) { + szErr[0] = '\0'; + } + esyslog("imageplugin: can't get mtime from %s (%d)%s.\n",szFileName,nErr,szErr); + } + } + } + } + } +#endif + +#if VDRVERSNUM < 10307 + Interface->Flush(); +#else + m_pDisplayReplay->Flush(); +#endif + free(sz); +} + + + + + +////////////////////////////////////////////////////////////////////////////// +/** VDR-Callback entry for Key was processed +@return eOSState +@param eKeys Key - the processed Keycode +*/ +eOSState cImageControl::ProcessKey(eKeys nKey) +{ + // Check for next image + SlideImage(); + + eOSState eOSRet = osContinue; + + if(m_pImageMenu + || (m_ePlayMode != ePlayModeJump && (nKey == kRed))) + { + return ProcessKeyCommands(nKey); + } + else + { + switch(nKey) + { + //processed global keybindings + //None key, check for ErrorMsg from Worker + case kNone: if(player)player->ErrorMsg(); break; + case kOk: ToogleShowMode(); break; + // Toggle between Play/Pause + case kPlay: + case kPause: ToogleSlideShowActiv(); break; + // Stop Plugin + case kStop: + case kBlue: eOSRet = ProcessKeyStopped(); break; + + default: //processed PlayMode depends keybindings + { + switch(m_ePlayMode) + { + case ePlayModeNormal: eOSRet = ProcessKeyPlayMode(nKey); break; + case ePlayModeZoom: eOSRet = ProcessKeyZoomMode(nKey); break; + case ePlayModeJump: eOSRet = ProcessKeyJumpMode(nKey); break; + } + } + } + } + // show current state at OSD + if(eOSRet == osContinue) + { + ShowOSD(); + ShowStatusMsg(); + } + return eOSRet; +} + +////////////////////////////////////////////////////////////////////////////// +/** Handle a Key stroke on normal playmode +@return eOSState +@param eKeys Key - the processed Keycode +*/ +eOSState cImageControl::ProcessKeyPlayMode(eKeys Key) +{ + switch (Key) + { + // Mode select + case kBack: return ProcessKeyStopped(); + case k0: OriginalImage(false); break; + case k8: ProcessKeyBeginJump(); break; + case k5: ProcessKeyBeginZoom(); break; + + // Rotate the image + case k1: LFlipImage(); break; + case k3: RFlipImage(); break; + + // Change time how long image is see + case k4|k_Repeat: + case k4: DecSlideTime(); break; + case k6|k_Repeat: + case k6: IncSlideTime(); break; + + // Navigate between images + case kLeft: PrevImage(1); break; + case kRight:NextImage(1); break; + case kDown: PrevImage(3); break; + case kUp: NextImage(3); break; + case k7: PrevImage(5); break; + case k9: NextImage(5); break; + + default: break; + } + + return osContinue; +} + +////////////////////////////////////////////////////////////////////////////// +/** Handle a Key stroke on jumpmode +@return eOSState +@param eKeys Key - the processed Keycode +*/ +eOSState cImageControl::ProcessKeyJumpMode(eKeys nKey) +{ + switch (nKey) + { + case kBack: // Restore Originalimage + case k0: OriginalImage(true); break; + + case k1...k9: //Picture select + { + const int nPicture = nKey - k1; + NextImage(nPicture); + break; + } + + // Navigate between images + case kLeft: ConvertJump(-9); break; + case kRight: ConvertJump(9); break; + + default: break; + } + + return osContinue; +} + +////////////////////////////////////////////////////////////////////////////// +/** Handle a Key stroke on jumpmode +@return eOSState +@param eKeys Key - the processed Keycode +*/ +eOSState cImageControl::ProcessKeyZoomMode(eKeys nKey) +{ + switch (nKey) + { + // Mode select + case k0: OriginalImage(true); break; + case kBack: + case k8: ZoomOut(); break; + case k5: ZoomIn(); break; + + // Navigate inside image + case kLeft: MoveZoomWindow(eLeft); break; + case kRight:MoveZoomWindow(eRight); break; + case kUp: MoveZoomWindow(eUp); break; + case kDown: MoveZoomWindow(eDown); break; + + default: break; + } + + return osContinue; +} + +////////////////////////////////////////////////////////////////////////////// +/** Zoom inside a image +*/ +void cImageControl::ZoomIn() +{ + if(m_nZoomFactor < (m_nZoomMin + 5)) //Allow 5 Zoomlevel + { + ++m_nZoomFactor; + ConvertZoom(); + } +} + +////////////////////////////////////////////////////////////////////////////// +/** Zoom outside a image +*/ +void cImageControl::ZoomOut() +{ + if(m_nZoomFactor > m_nZoomMin) + { + --m_nZoomFactor; + ConvertZoom(); + } + else + { + OriginalImage(true); + } +} + +////////////////////////////////////////////////////////////////////////////// +/** Navigate inside a zoomed image +@param eDirection nDirection - the wished navigation direction +*/ +void cImageControl::MoveZoomWindow(eDirection nDirection) +{ + bool bReZoomImage = false; + + switch(nDirection) + { + case eRight: + if(m_nZoomXMax > 0 && m_nMoveStepX < (m_nMaxStepX-1)) + { + ++m_nMoveStepX; + bReZoomImage = true; + } + break; + case eLeft: + if(m_nZoomXMax > 0 && m_nMoveStepX > (m_nMaxStepX-1)*-1) + { + --m_nMoveStepX; + bReZoomImage = true; + } + break; + case eDown: + if(m_nZoomYMax > 0 && m_nMoveStepY < (m_nMaxStepY-1)) + { + ++m_nMoveStepY; + bReZoomImage = true; + } + break; + case eUp: + if(m_nZoomYMax > 0 && m_nMoveStepY > (m_nMaxStepY-1)*-1) + { + --m_nMoveStepY; + bReZoomImage = true; + } + break; + } + if(bReZoomImage) + ConvertZoom(); +} + +////////////////////////////////////////////////////////////////////////////// +/** Check if key for pluginstop was processed +@return eOSState +@param eKeys Key - the processed Keycode +*/ +eOSState cImageControl::ProcessKeyStopped() +{ + if(player) + { + if(ePlayModeNormal != m_ePlayMode) + { + OriginalImage(true); + return osContinue; + } + + if(IsConvertRunning()) + return osContinue; + } + + HideOSD(); + Stop(); + return osEnd; +} + + +////////////////////////////////////////////////////////////////////////////// +/** Key was processed for Jumpmode +*/ +void cImageControl::ProcessKeyBeginJump() +{ + m_bSlideShowBefore = m_bSlideShowActiv; + + m_bSlideShowActiv = false; + + ConvertJump(0); + m_ePlayMode = ePlayModeJump; +} + +////////////////////////////////////////////////////////////////////////////// +/** Key was processed for Zoommode +*/ +void cImageControl::ProcessKeyBeginZoom() +{ + m_bSlideShowBefore = m_bSlideShowActiv; + + m_bSlideShowActiv = false; + PictureZoomInitial(); + m_ePlayMode = ePlayModeZoom; +} + + ////////////////////////////////////////////////////////////////////////////// +/** Toogle between all supported OSD Modes +*/ +void cImageControl::ToogleShowMode(void) +{ + switch(m_eOSDStatusVisable) + { + case eDisplayNothing: m_eOSDStatusVisable = eDisplayModeOnly; break; + case eDisplayModeOnly: m_eOSDStatusVisable = eDisplayProgress; break; + case eDisplayProgress: m_eOSDStatusVisable = eDisplayNothing; break; + } +} + +////////////////////////////////////////////////////////////////////////////// +/** Toogle between Play and Stop of the current SlideShow +*/ +void cImageControl::ToogleSlideShowActiv(void) +{ + m_nZoomFactor = 0; + m_bSlideShowActiv = !m_bSlideShowActiv; + m_ePlayMode = ePlayModeNormal; +} + +////////////////////////////////////////////////////////////////////////////// +/** Handle a Key stroke on commandsmenu +@return eOSState +@param eKeys Key - the processed Keycode +*/ +eOSState cImageControl::ProcessKeyCommands(eKeys nKey) +{ + if(m_pImageMenu) + { + eOSState eOSRet = m_pImageMenu->ProcessKey(nKey); + switch(eOSRet) + { + case osEnd: + case osBack: + if(m_pImageMenu->HasImageChanged()) + OriginalImage(false); + delete m_pImageMenu; + m_pImageMenu = NULL; + return osContinue; + default: + return eOSRet; + } + } + else + { + if(!CheckAccess()) { + OSD_ErrorNumMsg(errno,tr("Operation failed")); + return osContinue; + } + + cImageCommands* pCmd = new cImageCommands; + if(!pCmd) + return osContinue; + + // Load additional Commands + pCmd->Load(AddDirectory(cPlugin::ConfigDirectory(), "imagecmds.conf")); + + if(pCmd->Count() <= 0) { + delete pCmd; + return osContinue; + } + + Hide(); + + const char* szFileName = FileName(); + char* szTitle; + asprintf(&szTitle,"%s (%s)",tr("Commands"),basename(szFileName)); + m_pImageMenu = new cImageMenuCommands(szTitle,pCmd,szFileName); + free(szTitle); + + return osContinue; + } +} + +////////////////////////////////////////////////////////////////////////////// +/** Check to get access for to viewed file +@return bool - false access was denied +*/ +bool cImageControl::CheckAccess() const +{ + if(!player) // Playback stopped ? + { + errno = EFAULT; + return false; + } + + errno = 0; // unset error + const char *szFileName = player->FileName(); + if(szFileName + && 0 == access(szFileName,F_OK)) + { + return true; + } + if(!errno) // if unknown error + errno = ENOENT; + return false; +} + +void cImageControl::OriginalImage(bool bCached) +{ + m_nZoomFactor = 0; + m_ePlayMode = ePlayModeNormal; + if(!CheckAccess() + || !player->Convert(bCached?"":"original")) + { + OSD_ErrorNumMsg(errno,tr("Operation failed")); + } +} + +void cImageControl::RFlipImage(void) +{ + m_ePlayMode = ePlayModeNormal; + if(!CheckAccess() + || !player->Convert("right")) + { + OSD_ErrorNumMsg(errno,tr("Operation failed")); + } +} + +void cImageControl::LFlipImage(void) +{ + m_ePlayMode = ePlayModeNormal; + if(!CheckAccess() + || !player->Convert("left")) + { + OSD_ErrorNumMsg(errno,tr("Operation failed")); + } +} + +void cImageControl::PictureZoomInitial(void) +{ + + char *szFileName; + FILE *f; + char buf[80]; + + cImage* pImage = theSlideShow.GetImage(); + if(!pImage) + return; + + asprintf(&szFileName, "%s.par", pImage->NamePNM()); + + if(!szFileName) + return; + + strcpy(zoom_command, "original"); + + m_nRealImageWidth = 720; + m_nRealImageHeight = 576; + + if((f = fopen(szFileName, "rt"))) + { + dsyslog("imageplugin: open file %s", szFileName); + fgets(buf, sizeof(buf) - 1, f); + dsyslog("imageplugin: line=%s", buf); + sscanf(buf, "%d %d %s", &m_nRealImageWidth,&m_nRealImageHeight,zoom_command); + fclose(f); + } + else + { + esyslog("imageplugin: error by open file %s", szFileName); + } + + free(szFileName); + + if(m_nRealImageWidth <= 0 + || m_nRealImageWidth > 720 + || m_nRealImageHeight > 576 ) + m_nZoomMin = 1; + else + { + m_nZoomMin = 800/m_nRealImageWidth; + } + if (m_nZoomMin < 0) + m_nZoomMin = 1; + + // Start with minimum zoom + m_nZoomFactor = m_nZoomMin; + // Set Move to center + m_nMoveStepX = 0; + m_nMoveStepY = 0; + + ConvertZoom(); +} + +void cImageControl::ConvertZoom() +{ + if(!player) + return; + + m_ePlayMode = ePlayModeZoom; + + // How may pixel are outside screen after zoom + m_nZoomXMax = ((m_nRealImageWidth * m_nZoomFactor) - player->UseWidth()); + m_nZoomYMax = ((m_nRealImageHeight * m_nZoomFactor) - player->UseHeight()); + + // If image bigger than screen, how many step can i'm move in zoomed image + if(m_nZoomXMax > 0) + { + m_nMaxStepX = (m_nRealImageWidth * m_nZoomFactor) / player->UseWidth() * 2; + if(m_nMoveStepX >= m_nMaxStepX) + m_nMoveStepX = m_nMaxStepX; + if(m_nMoveStepX <= -m_nMaxStepX) + m_nMoveStepX = (m_nMaxStepX)*-1; + } + else + { + m_nMoveStepX = 0; + m_nMaxStepX = 0; + } + + if(m_nZoomYMax > 0) + { + m_nMaxStepY = (m_nRealImageHeight * m_nZoomFactor) / player->UseHeight() * 2; + if(m_nMoveStepY >= m_nMaxStepY) + m_nMoveStepY = m_nMaxStepY-1; + if(m_nMoveStepY <= -m_nMaxStepY) + m_nMoveStepY = (m_nMaxStepY)*-1; + } + else + { + m_nMoveStepY = 0; + m_nMaxStepY = 0; + } + + // Set Offset to center + int nZoomXoff = m_nZoomXMax/2; + int nZoomYoff = m_nZoomYMax/2; + + // Add to offset the moved position + if(m_nMaxStepX > 0) + nZoomXoff += (m_nZoomXMax/m_nMaxStepX)*m_nMoveStepX; + if(m_nMaxStepY > 0) + nZoomYoff += (m_nZoomYMax/m_nMaxStepY)*m_nMoveStepY; + + // execute + if(!CheckAccess() + || !player->ConvertZoom(zoom_command, m_nZoomFactor, nZoomXoff, nZoomYoff)) + { + OSD_ErrorNumMsg(errno,tr("Operation failed")); + } +} + +void cImageControl::ConvertJump(int nOffset) +{ + m_ePlayMode = ePlayModeJump; + if(!CheckAccess() + || !player->ConvertJump(nOffset)) + { + OSD_ErrorNumMsg(errno,tr("Operation failed")); + } +} + +bool cImageControl::IsConvertRunning() const +{ + if(player) + return player->IsConvertRunning(); + return false; +} + + +void cImageControl::NextImage(int Step) +{ + if(!player) + return; + if(ePlayModeNormal != m_ePlayMode) + { + m_ePlayMode = ePlayModeNormal; + m_bSlideShowActiv = m_bSlideShowBefore; + } + if(player->NextImage(Step)) + OriginalImage(true); + m_tStarted = time(NULL); +} + + + +void cImageControl::PrevImage(int Step) +{ + if(!player) + return; + if(ePlayModeNormal != m_ePlayMode) + { + m_ePlayMode = ePlayModeNormal; + m_bSlideShowActiv = m_bSlideShowBefore; + } + if(player->PrevImage(Step)) + OriginalImage(true); + m_tStarted = time(NULL); +} + + +void cImageControl::GotoImage(int Pict) +{ + if(!player) + return; + if(ePlayModeNormal != m_ePlayMode) + { + m_ePlayMode = ePlayModeNormal; + m_bSlideShowActiv = m_bSlideShowBefore; + } + if(player->GotoImage(Pict)) + OriginalImage(true); + m_tStarted = time(NULL); +} + + +int cImageControl::ImageTotal(void) const +{ + if(!player) + return 0; + return theSlideShow.ImageTotal(); +} + +int cImageControl::ImageCurrent(void) const +{ + if(!player) + return 0; + return theSlideShow.ImageCurrent(); +} + +const char* cImageControl::FileName(void) const +{ + if(!player) + return 0; + return player->FileName(); +} + + + +void cImageControl::SlideImage() +{ + if(m_bSlideShowActiv && !IsConvertRunning()) + { + if(ImageSetup.SSsec <= + (time(NULL) - m_tStarted)) + { + NextImage(1); + } + } +} + +void cImageControl::Stop(void) +{ + delete player; + player = NULL; +} + +void cImageControl::IncSlideTime(void) +{ + if(ImageSetup.SSsec < cImageSetup::m_cSSMax) { + ImageSetup.SSsec++; + } +} + +void cImageControl::DecSlideTime(void) +{ + if(ImageSetup.SSsec > cImageSetup::m_cSSMin) { + ImageSetup.SSsec--; + } +} diff --git a/control-image.h b/control-image.h new file mode 100644 index 0000000..a09c02f --- /dev/null +++ b/control-image.h @@ -0,0 +1,169 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold + * based on (C) 2003 Kai Tobias Burwieck + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +// --- cImageControl --------------------------------------------------------- + +#ifndef ___DVB_IMAGE_CONTROL_H +#define ___DVB_IMAGE_CONTROL_H + +#include +#if VDRVERSNUM >= 10307 +#include +#endif + +class cImagePlayer; +class cImageMenuCommands; + +class cImageControl + : public cControl +{ + /** All supported Replaymodes */ + enum ePlayMode { ePlayModeNormal, ePlayModeZoom, ePlayModeJump }; + /** All supported OSD Modes */ + enum eOSDStatusMode { eDisplayNothing, eDisplayModeOnly, + eDisplayProgress + }; + /** All move directions to navigate inside zoomed image */ + enum eDirection {eLeft = 1,eRight,eUp,eDown}; + + /** Our one and only Imageplayer, don't rename it see cControl */ + cImagePlayer *player; + /** Interface to Menu with Usercommands*/ + cImageMenuCommands *m_pImageMenu; + /** Current playing mode (Jump <-> Normal <-> Zoom) */ + ePlayMode m_ePlayMode; + /** Remember the active Slideshow mode before Jump or Zoom used */ + bool m_bSlideShowBefore; + /** What should on OSD visable (Nothing <-> ModeOnly <-> Progress) */ + eOSDStatusMode m_eOSDStatusVisable; + /** Who was OSD Interface open (Nothing <-> ModeOnly <-> Progress) */ + eOSDStatusMode m_eOSDStatusIsOpen; + /** View our picture in a slideshow */ + bool m_bSlideShowActiv; + /** date at image was open*/ + time_t m_tStarted; + /** Last Message for Statusmonitor */ + static char* m_szLastShowStatusMsg; +#if VDRVERSNUM >= 10307 + /** Display replayprogress*/ + cSkinDisplayReplay *m_pDisplayReplay; + /** Faked Marks for Images*/ + cMarks m_Marks; +#endif + + /** zoom factor on zoom mode */ + int m_nZoomFactor; + /** minimum zoom factor (1... */ + int m_nZoomMin; + /** maximum value move steps on zoom on width */ + int m_nMaxStepX; + /** maximum value move steps on zoom on height */ + int m_nMaxStepY; + /** current value move steps on zoom on width, 0 is center*/ + int m_nMoveStepX; + /** current value move steps on zoom on height, 0 is center*/ + int m_nMoveStepY; + /** How may pixel are outside screen after zoom, on width */ + int m_nZoomXMax; + /** How may pixel are outside screen after zoom, on height */ + int m_nZoomYMax; + /** real image pixel width*/ + int m_nRealImageWidth; + /** real image pixel height*/ + int m_nRealImageHeight; + + char zoom_command[20]; + +private: + void ShowOSD(void); + void HideOSD(void); + void ShowMode(void); +#if VDRVERSNUM < 10307 + void DisplayAtBottom(const char *s); +#endif + void ShowProgress(void); + /** Send Message if changed to any statusmonitor */ + void ShowStatusMsg(); + + void PictureZoomInitial(void); + + void ProcessKeyBeginJump(); + void ProcessKeyBeginZoom(); + + /** Deliver the Status of running convert-script */ + bool IsConvertRunning() const; + /** Deliver the current number of viewed Image */ + int ImageCurrent(void) const; + /** Deliver the total number of viewed Image */ + int ImageTotal(void) const; + /** Deliver the filename from the current number of viewed Image */ + const char* FileName() const; + + /** Check if key for pluginstop was processed*/ + eOSState ProcessKeyStopped(); + /** Toogle between all supported OSD Modes */ + void ToogleShowMode(void); + /** Toogle between Play and Stop of the current SlideShow */ + void ToogleSlideShowActiv(void); + + /** Zoom inside a image*/ + void ZoomIn(); + /** Zoom outside a image*/ + void ZoomOut(); + /** Navigate inside a zoomed image */ + void MoveZoomWindow(eDirection nDirection); + + eOSState ProcessKeyPlayMode(eKeys nKey); + eOSState ProcessKeyJumpMode(eKeys nKey); + eOSState ProcessKeyZoomMode(eKeys nKey); + eOSState ProcessKeyCommands(eKeys nKey); + + void NextImage(int Step); + void PrevImage(int Step); + void GotoImage(int Pict); + void RFlipImage(void); + void LFlipImage(void); + void OriginalImage(bool bCached); + void ConvertZoom(); + void ConvertJump(int Step); + + void IncSlideTime(void); + void DecSlideTime(void); + + bool CheckAccess() const; +private: + cImageControl(cSlideShow * pNewSlideShow); +public: + virtual ~ cImageControl(); + + static void SetSlideShow(cSlideShow * pNewSlideShow); + + virtual eOSState ProcessKey(eKeys Key); + virtual void Hide(void); + virtual void Show(void); + +protected: + void SlideImage(); + void Stop(void); +}; + +#endif //___DVB_IMAGE_CONTROL_H diff --git a/data-image.c b/data-image.c new file mode 100644 index 0000000..f99ec76 --- /dev/null +++ b/data-image.c @@ -0,0 +1,266 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2003 Kai Tobias Burwieck + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include "data-image.h" +#include "data.h" +#include "setup-image.h" + +cFileSources ImageSources; + +// --- cImage ------------------------------------------------------------- + +cImage::cImage(const char *szName, cFileSource * pSource) +: m_pSource(pSource) +, m_szFileName(NULL) +, m_szFileNamePNM(NULL) +, m_szFileNameIndex(NULL) +, m_szFileNameZoom(NULL) +{ + if(szName) + m_szFileName = m_pSource->BuildName(szName); + m_pSource->Block(); +} + +cImage::~cImage() +{ + Clear(); + m_pSource->Unblock(); +} + +const char *cImage::NameIndex() +{ + if(!m_szFileNameIndex) + { + if(ImageSetup.m_bHousekeeping) + { + char sz[PATH_MAX]; + strncpy(sz,ImageSetup.TempDir,sizeof(sz)); + strncat(sz,"/IXXXXXX",sizeof(sz)); + mktemp(sz); + strncat(sz,".pnm",sizeof(sz)); + m_szFileNameIndex = strdup(sz); + } + else + { + asprintf(&m_szFileNameIndex, "%s%sI%s.pnm", + ImageSetup.TempDir, + *m_szFileName == '/'?"":"/", + m_szFileName); + } + } + return m_szFileNameIndex; +} + +const char *cImage::NamePNM() +{ + if(!m_szFileNamePNM) + { + if(ImageSetup.m_bHousekeeping) + { + char sz[PATH_MAX]; + strncpy(sz,ImageSetup.TempDir,sizeof(sz)); + strncat(sz,"/VXXXXXX",sizeof(sz)); + mktemp(sz); + strncat(sz,".pnm",sizeof(sz)); + m_szFileNamePNM = strdup(sz); + } + else + { + asprintf(&m_szFileNamePNM, "%s%s%s.pnm", + ImageSetup.TempDir, + *m_szFileName == '/'?"":"/", + m_szFileName); + } + } + return m_szFileNamePNM; +} + +const char *cImage::NameZoom() +{ + if(!m_szFileNameZoom) + { + if(ImageSetup.m_bHousekeeping) + { + char sz[PATH_MAX]; + strncpy(sz,ImageSetup.TempDir,sizeof(sz)); + strncat(sz,"/ZXXXXXX",sizeof(sz)); + mktemp(sz); + strncat(sz,".pnm",sizeof(sz)); + m_szFileNameZoom = strdup(sz); + } + else + { + asprintf(&m_szFileNameZoom, "%s%sZ%s.pnm", + ImageSetup.TempDir, + *m_szFileName == '/'?"":"/", + m_szFileName); + } + } + return m_szFileNameZoom; +} + +bool UnlinkFile(const char *szFile) +{ + struct stat st; + if(0 == stat(szFile,&st) && S_ISREG(st.st_mode) && 0 != unlink(szFile)) + { + char szErr[128]; + int nErr = errno; + szErr[sizeof(szErr)-1] = '\0'; + if(!nErr || 0 != strerror_r(nErr,szErr,sizeof(szErr)-1)) { + szErr[0] = '\0'; + } + esyslog("imageplugin: Can't remove temporary file %s, because: %s.",szFile,szErr[0] != '\0'?szErr:"unknown"); + return false; + } + return true; +} + +void cImage::Unlink(const char *szName) +{ + if(ImageSetup.m_bHousekeeping) + { + char sz[PATH_MAX]; + // remove /tmp/image/xxx.pnm + UnlinkFile(szName); + // remove /tmp/image/xxx.pnm.par + strncpy(sz,szName,sizeof(sz)); + strncat(sz,".par",sizeof(sz)); + UnlinkFile(sz); + // remove /tmp/image/xxx.pnm.tmp + strncpy(sz,szName,sizeof(sz)); + strncat(sz,".tmp",sizeof(sz)); + UnlinkFile(sz); + } +} + + +void cImage::Clear(void) +{ + if(m_szFileNameIndex) + { + Unlink(m_szFileNameIndex); + free(m_szFileNameIndex); + m_szFileNameIndex = NULL; + } + + if(m_szFileNameZoom) + { + Unlink(m_szFileNameZoom); + free(m_szFileNameZoom); + m_szFileNameZoom = NULL; + } + + if(m_szFileNamePNM) + { + Unlink(m_szFileNamePNM); + free(m_szFileNamePNM); + m_szFileNamePNM = NULL; + } + + if(m_szFileName) + { + free(m_szFileName); + m_szFileName = NULL; + } +} + +bool cImage::CompareBaseDir(const cFileSource * pSource) const +{ + return 0 == strcmp(m_pSource->BaseDir(),pSource->BaseDir()); + +} +// -- cSlideShow -------------------------------------------------------------- + +cSlideShow::cSlideShow(cDirItem * Item) +: m_szFirstImageName(NULL) +, m_DirItem(Item->Source, Item->Subdir, Item->Name, Item->Type) +{ + +} + +cSlideShow::~cSlideShow() +{ + if(m_szFirstImageName) + free(m_szFirstImageName); +} + +bool cSlideShow::Load(void) +{ + bool res = false; + Clear(); + switch (m_DirItem.Type) + { + case itFile: + { + dsyslog("imageplugin: SlideShow: file %s\n", m_DirItem.Name); + const char *path = m_DirItem.Subdir; + res = + ScanDir(m_DirItem.Source, path, stFile, m_DirItem.Source->Include(), + 0, true); + if(res) + { + m_szFirstImageName = strdup(m_DirItem.Name); + } + else + { + DoItem(m_DirItem.Source, m_DirItem.Subdir, m_DirItem.Name); + res = true; + } + break; + } + case itDir: + { + dsyslog("imageplugin: SlideShow: dir name:%s\n", m_DirItem.Name); + char *path = m_DirItem.Path(); + res = + ScanDir(m_DirItem.Source, path, stFile, m_DirItem.Source->Include(), + 0, true); + free(path); + break; + } + case itBase: + dsyslog("imageplugin: SlideShow: base\n"); + res = + ScanDir(m_DirItem.Source, 0, stFile, m_DirItem.Source->Include(), 0, + true); + break; + default: + break; + } + return res; +} + +void cSlideShow::DoItem(cFileSource * src, const char *subdir, + const char *name) +{ + char *path = (char *)name; + if(subdir) + path = AddPath(subdir, name); + Add(new cImage(path, src)); + if(subdir) + free(path); +} diff --git a/data-image.h b/data-image.h new file mode 100644 index 0000000..b21ba4b --- /dev/null +++ b/data-image.h @@ -0,0 +1,87 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2003 Kai Tobias Burwieck + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef ___DATA_IMAGE_H +#define ___DATA_IMAGE_H + +#include "data.h" + +#include + +// ---------------------------------------------------------------- + +extern cFileSources ImageSources; + +class cFileSource; + +// ---------------------------------------------------------------- + +class cImage +: public cListObject { + + cFileSource *m_pSource; + char *m_szFileName; + + char *m_szFileNamePNM; + char *m_szFileNameIndex; + char *m_szFileNameZoom; + +protected: + void Unlink(const char *szName); + void Clear(void); +public: + cImage(const char *szName, cFileSource * pSource); + virtual ~cImage(); + bool CompareBaseDir(const cFileSource * pSource) const; + + inline const char *Name(void) const + { + return m_szFileName; + } + const char *NameZoom(); + const char *NamePNM(); + const char *NameIndex(); + +}; + +// ---------------------------------------------------------------- + + +class cSlideShow +: public cScanDir +, public cList < cImage > { + + char* m_szFirstImageName; + cDirItem m_DirItem; + +protected: + + virtual void DoItem(cFileSource * src, const char *subdir, const char *name); + +public: + cSlideShow(cDirItem * Item); + virtual ~cSlideShow(); + virtual bool Load(void); + + inline const char* FirstImage() const { return m_szFirstImageName; } +}; + +#endif //___DATA_IMAGE_H diff --git a/data.c b/data.c new file mode 100644 index 0000000..59605fb --- /dev/null +++ b/data.c @@ -0,0 +1,508 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2003 Kai Tobias Burwieck + * + * based on MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "data.h" +#include "data-image.h" +#include "list.h" + +#include + +// ---------------------------------------------------------------- + +const char *g_szMountScript = "mount.sh"; + +char *AddPath(const char *dir, const char *filename) +{ + int l = strlen(dir); + char *name = MALLOC(char, l + strlen(filename) + 2); + if(name) + { + strcpy(name, dir); + name[l] = '/'; + strcpy(name + l + 1, filename); + } else { + esyslog("imageplugin: ERROR: no memory for filename"); + } + return name; +} + +class cFileExt : public cListObject +{ + char * m_szExt; +protected: + void clean() { if(Ext()) free(m_szExt);m_szExt=0;} +public: + cFileExt() { m_szExt = 0; } + virtual ~cFileExt() { clean(); } + + cFileExt(cFileExt& x) //Copy-Ctor + { + if(x.Ext()) + { + m_szExt = MALLOC(char, strlen(x.m_szExt)+1); + strcpy(m_szExt,x.m_szExt); + } + else m_szExt = 0; + } + const char* Ext() const { return m_szExt; } + + const char* Set(const char* sz) + { + const char *f = sz; + for(;*sz != 0 && *sz == ' ';++sz); //Skip leading whitespace + for(f = sz;*f != 0 && *f != ' ';++f); //Find end of item \0 or ' ' + + clean(); //Prevebt double assign + + if(f - sz > 0) // Store founded item + { + m_szExt = MALLOC(char, f - sz + 1); + strncpy(m_szExt,sz,f - sz); + *(m_szExt + (f - sz)) = '\0'; + } + return f; + } +}; + +class cFileExtList : public cList +{ +public: + cFileExtList(const char* szExtList) { + const char* sz = szExtList; + while(*sz != 0) + { + cFileExt ext; + sz = ext.Set(sz); + if(ext.Ext()) + Add(new cFileExt(ext)); // Add copy + }; + } + + virtual ~cFileExtList() + { + + } +}; + + +// -- cScanDir -------------------------------------------------------------- + +bool cScanDir::ScanDir(cFileSource * src, const char *subdir, eScanType type, + const char *spec, const char *excl, bool recursiv) +{ + bool result = true; + char *cmd = 0, *dir = 0, *s = 0, *e = 0, tc; + + switch (type) + { + default: + case stFile: + tc = 'f'; + break; + case stDir: + tc = 'd'; + break; + } + if(subdir) + asprintf(&dir, "%s/%s", src->BaseDir(), subdir); + else + asprintf(&dir, "%s", src->BaseDir()); + // If is'nt set a filter use this a default + if(stFile == type && (0 == spec || 0>= strlen(spec))) + spec = "*.jpg *.jpeg *.jif *.jiff *.tif *.tiff *.gif *.bmp *.png *.pnm *.mps"; + // Check if filter is set build complex include find ( -iname "*.jpg" -o -iname "*.jpeg" -o .. ) + if(stFile == type && spec) + { + cFileExtList list(spec); //Splitt *.jpg *.jpeg + + cFileExt *src = list.First(); + while(src) //Loop throw all found item + { + char* sn; + + if(!s) //open bracket + asprintf(&s, " \\( "); + + asprintf(&sn, "%s-iname \"%s\" ",s,QuoteString(src->Ext())); + if(s) free(s); + s = sn; + + src = list.Next(src); + if(src) //follow ext + { + char* sn; + asprintf(&sn, "%s-o ",s); + free(s); + s = sn; + } + } + if(s) // close bracket + { + char* sn; + asprintf(&sn, "%s\\)",s); + free(s); + s = sn; + } + } + // Check if filter is set build complex exclude find -not ( -iname "*.jpg" -o -iname "*.jpeg" -o .. ) + if(stFile == type && excl) { + cFileExtList list(excl); //Splitt *.jpg *.jpeg + + cFileExt *src = list.First(); + while(src) //Loop throw all found item + { + char* en; + + if(!e) //open bracket + asprintf(&e, "-not \\( "); + + asprintf(&en, "%s-iname \"%s\" ",e,QuoteString(src->Ext())); + if(e) free(e); + e = en; + + src = list.Next(src); + if(src) //follow ext + { + char* en; + asprintf(&en, "%s-o ",e); + free(e); + e = en; + } + } + if(e) // close bracket + { + char* en; + asprintf(&en, "%s\\)",e); + free(e); + e = en; + } + } +#if 0 + asprintf(&cmd, "find \"%s\" -follow -type %c %s %s %s 2>/dev/null | sort -df", + QuoteString(dir), tc, s?s:"", e?e:"", recursiv?"":"-maxdepth 1"); +#else + asprintf(&cmd, "find \"%s\" -follow -type %c %s %s %s 2>/dev/null | sort -df | grep -v \"/\\.\"", + QuoteString(dir), tc, s?s:"", e?e:"", recursiv?"":"-maxdepth 1"); +#endif + //fprintf(stderr,"%s\n",cmd); +#if VDRVERSNUM >= 10318 + cReadLine l; +#endif + FILE *p = popen(cmd, "r"); + if(p) { + int len = strlen(dir); + char *s; +#if VDRVERSNUM >= 10318 + while((s = l.Read(p)) != 0) { +#else + while((s = readline(p)) != 0) { +#endif + char *ss = strstr(s, dir); + if(ss) { + s = ss + len; + if(*s == '/') + s++; + } + if(*s) + DoItem(src, subdir, s); + } + pclose(p); + } + else + result = false; + + free(cmd); + free(dir); + free(s); + free(e); + return result; +} + +char *cScanDir::QuoteString(const char *str) +{ + static char *nstr = 0; + + free(nstr); + nstr = MALLOC(char, strlen(str) * 2); + char *p = nstr; + while(*str) + { + switch (*str) + { + case '$': // dollar + case '\\': // backslash + case '\"': // double quote + case '`': // back tick + *p++ = '\\'; + // fall through + default: + *p++ = *str++; + break; + } + } + *p = 0; + return nstr; +} + +// -- cDirItem -------------------------------------------------------------- + +cDirItem::cDirItem(cFileSource * src, const char *subdir, const char *name, + const eItemType type) +{ + Source = src; + Subdir = subdir ? strdup(subdir) : 0; + Name = name ? strdup(name) : 0; + Type = type; +} + +cDirItem::~cDirItem() +{ + free(Name); + free(Subdir); +} + +char *cDirItem::Path(void) +{ + char *path; + if(Subdir) + path = AddPath(Subdir, Name); + else + path = strdup(Name); + return path; +} + +// -- cDirList -------------------------------------------------------------- + +bool cDirList::Load(cFileSource * src, const char *subdir) +{ + bool res = false; + Clear(); + if(subdir) + Add(new cDirItem(src, subdir, "..", itParent)); + itype = itDir; + if(ScanDir(src, subdir, stDir, 0, 0, false)) { + itype = itFile; + if(ScanDir(src, subdir, stFile, src->Include(), 0, false)) + res = true; + } + return res; +} + +void cDirList::DoItem(cFileSource * src, const char *subdir, const char *name) +{ + Add(new cDirItem(src, subdir, name, itype)); +} + + + +// -- cFileSource -------------------------------------------------------------- + +cFileSource::cFileSource(void) +{ + browsedir = browseparent = 0; + basedir = description = include = 0; + useCount = 0; + needsmount = false; +} + +cFileSource::cFileSource(const char *Basedir, const char *Description, + const bool NeedsMount, const char *Include) +{ + browsedir = browseparent = 0; + basedir = description = include = 0; + useCount = 0; + Set(Basedir, Description, NeedsMount, Include); +} + +cFileSource::~cFileSource() +{ + ClearRemember(); + free(basedir); + free(description); + free(include); +} + +void cFileSource::Set(const char *Basedir, const char *Description, + const bool NeedsMount, const char *Include) +{ + free(basedir); + basedir = strdup(Basedir); + free(description); + description = strdup(Description); + free(include); + include = Include ? strdup(Include) : 0; + needsmount = NeedsMount; +} + +char *cFileSource::BuildName(const char *filename) +{ + return AddPath(basedir, filename); +} + +void cFileSource::SetRemember(const char *dir, const char *parent) +{ + ClearRemember(); + if(dir) + browsedir = strdup(dir); + if(parent) + browseparent = strdup(parent); +} + +void cFileSource::ClearRemember(void) +{ + free(browsedir); + browsedir = 0; + free(browseparent); + browseparent = 0; +} + +bool cFileSource::GetRemember(char *&dir, char *&parent) +{ + dir = parent = 0; + if(browsedir) { + if(browseparent) + parent = strdup(browseparent); + dir = strdup(browsedir); + return true; + } + return false; +} + +bool cFileSource::Parse(char *s) +{ + char base[256], des[256], incl[256]; + int needsmount, n; + if((n = + sscanf(s, "%255[^;];%255[^;];%d;%255[^;]", base, des, &needsmount, + incl)) >= 3) + { + char *base2 = skipspace(stripspace(base)); + Set(base2, skipspace(stripspace(des)), needsmount != 0, + n > 3 ? skipspace(stripspace(incl)) : 0); + + // do some checking of the basedir and issue a warning if apropriate + if(access(base2, R_OK)) { + esyslog("imageplugin: WARNING: source base %s not found/permission denied",base2); + } + else { + struct stat64 ds; + if(lstat64(base2, &ds)) { + esyslog("imageplugin: WARNING: can't stat source base %s",base2); + } + else { + if(S_ISLNK(ds.st_mode)) { + esyslog("imageplugin: WARNING: source base %s is a symbolic link",base2); + } + else if(!S_ISDIR(ds.st_mode)) { + esyslog("imageplugin: WARNING: source base %s is not a directory",base2); + } + } + } + return true; + } + return false; +} + +bool cFileSource::Action(eAction act) +{ + static char *str[] = { "mount", "unmount", "eject", "status" }; + + char *cmd = 0; + asprintf(&cmd, "%s %s %s", g_szMountScript, str[act], basedir); + bool res = (system(cmd) == 0); + free(cmd); + return res; +} + +bool cFileSource::Mount(void) +{ + bool res = false; + if(needsmount && (res = Action(acMount))) + ClearRemember(); + return res; +} + +bool cFileSource::Unmount(void) +{ + bool res = false; + if(needsmount) { + + theSlideShow.Remove(this); + + if(!useCount && (res = Action(acUnmount))) + ClearRemember(); + } + return res; +} + +bool cFileSource::Eject(void) +{ + bool res = false; + if(needsmount) { + + theSlideShow.Remove(this); + + if(!useCount && (res = Action(acEject))) + ClearRemember(); + } + return res; +} + +bool cFileSource::Status(void) +{ + if(needsmount) + return Action(acStatus); + return true; +} + +// -- cFileSources -------------------------------------------------------------- + +bool cFileSources::Load(const char *filename, bool dummy) +{ + if(cConfig < cFileSource >::Load(filename, true)) { + SetSource(First()); + return true; + } + return false; +} + +cFileSource *cFileSources::FindSource(const char *filename) +{ + cFileSource *src = First(); + while(src) { + if(startswith(filename, src->BaseDir())) + return src; + src = Next(src); + } + return 0; +} diff --git a/data.h b/data.h new file mode 100644 index 0000000..b6272bd --- /dev/null +++ b/data.h @@ -0,0 +1,128 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2003 Kai Tobias Burwieck +* + * based on MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + * + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef ___DATA_H +#define ___DATA_H + +#include +#include + +extern const char *g_szMountScript; + +// ---------------------------------------------------------------- + +class cFileSource; + +extern char *AddPath(const char *dir, const char *filename); + +// ---------------------------------------------------------------- + +class cScanDir { + char *QuoteString(const char *str); +protected: + enum eScanType { stFile, stDir }; + virtual void DoItem(cFileSource *src, const char *subdir, const char *name)=0; +public: + bool ScanDir(cFileSource *src, const char *subdir, eScanType type, const char *spec, const char *excl, bool recursiv); + }; + +// ---------------------------------------------------------------- + +enum eItemType { itDir, itParent, itFile, itBase }; + +class cDirItem +: public cListObject { +public: + cDirItem(cFileSource *src, const char *subdir, const char *name, const eItemType type); + ~cDirItem(); + char *Path(void); + // + cFileSource *Source; + char *Subdir; + char *Name; + eItemType Type; + }; + +// ---------------------------------------------------------------- + +class cDirList +: public cScanDir +, public cList { + eItemType itype; +protected: + virtual void DoItem(cFileSource *src, const char *subdir, const char *name); +public: + bool Load(cFileSource *src, const char *subdir); + }; + +// ---------------------------------------------------------------- + +class cFileSource +: public cListObject { + enum eAction { acMount, acUnmount, acEject, acStatus }; + char *basedir; + char *description; + char *include; + bool needsmount; + int useCount; + // remember last browse position + char *browsedir, *browseparent; + // + void Set(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include); + bool Action(eAction act); + void ClearRemember(void); +public: + cFileSource(void); + cFileSource(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include=0); + ~cFileSource(); + bool Parse(char *s); + bool Mount(void); + bool Unmount(void); + bool Eject(void); + bool Status(void); + void Block(void) { ++useCount; } + void Unblock(void) { --useCount; } + char *BuildName(const char *filename); + void SetRemember(const char *dir, const char *parent); + bool GetRemember(char * &dir, char * &parent); + const char *BaseDir(void) const { return basedir; } + const char *Description(void) const { return description; } + const char *Include(void) const { return include; } + bool NeedsMount(void) const { return needsmount; } + }; + +// ---------------------------------------------------------------- + +class cFileSources +: public cConfig { + cFileSource *current; +public: + virtual bool Load(const char *filename, bool dummy=false); + void SetSource(cFileSource *source) { current=source; } + cFileSource *GetSource(void) { return current; } + cFileSource *FindSource(const char *filename); + }; + +#endif //___DATA_H diff --git a/examples/imagecmds.conf b/examples/imagecmds.conf new file mode 100644 index 0000000..779bd99 --- /dev/null +++ b/examples/imagecmds.conf @@ -0,0 +1,22 @@ +# +# This is a example of imagecmds.conf with with usually using and sample entrys. +# +# This file should placed on VDR configuration folder with setup.conf +# .../setup.conf +# .../plugins/imagecmds.conf +# +# Syntax is: [?]; +# +# = descriptor displayed in VDR +# [?] = confirmation request, before execute command (optionally) +# = command what execute with current watched file +# %s - placeholder filename (optionally) +# +# See also at man-pages vdr(5), it the same syntax as reccmds.conf +# +Informations about picture : identify -verbose +Size of picture : du -chs %s +Dump Exif informations from JPEG-picture : jpegtopnm -dumpexif %s >/dev/null 2> $CONVERT_TEMPDIR/exif.tmp && cat $CONVERT_TEMPDIR/exif.tmp && rm -f $CONVERT_TEMPDIR/exif.tmp +Rotate JPEG picture lossless by 90 degrees to the right : jpegtran -rotate 90 %s > $CONVERT_TEMPDIR/tmp.jpg && mv $CONVERT_TEMPDIR/tmp.jpg %s +Rotate JPEG picture lossless by 90 degrees to the left : jpegtran -rotate 270 %s > $CONVERT_TEMPDIR/tmp.jpg && mv $CONVERT_TEMPDIR/tmp.jpg %s +Delete picture ?: rm -f %s diff --git a/examples/imagecmds.conf.DE b/examples/imagecmds.conf.DE new file mode 100644 index 0000000..71d21bf --- /dev/null +++ b/examples/imagecmds.conf.DE @@ -0,0 +1,16 @@ +# +# Kommandos für Image plugins +# +# Format: see also man vdr(5) +# +## Menutext ?: Kommando %s +# +# ? - Bestätigung anfordern, (optional) +# %s - Platzhalter für Dateinamen (optional) +# +Informationen über das Bild : identify -verbose +Größe des Bildes : du -chs %s +Exif Informationen des JPEG-Bildes ausgeben : jpegtopnm -dumpexif %s >/dev/null 2> $CONVERT_TEMPDIR/exif.tmp && cat $CONVERT_TEMPDIR/exif.tmp && rm -f $CONVERT_TEMPDIR/exif.tmp +Rotatiere JPEG Bildes verlustlos um 90° nach Rechts : jpegtran -rotate 90 %s > $CONVERT_TEMPDIR/tmp.jpg && mv $CONVERT_TEMPDIR/tmp.jpg %s +Rotatiere JPEG Bildes verlustlos um 90° nach Links : jpegtran -rotate 270 %s > $CONVERT_TEMPDIR/tmp.jpg && mv $CONVERT_TEMPDIR/tmp.jpg %s +Lösche Bild ?: rm -f %s diff --git a/examples/imagesources.conf b/examples/imagesources.conf new file mode 100644 index 0000000..0b3c441 --- /dev/null +++ b/examples/imagesources.conf @@ -0,0 +1,21 @@ +# +# This is a example of imagesources.conf with usually using and sample entrys. +# +# This file should placed on VDR configuration folder with setup.conf +# .../setup.conf +# .../plugins/imagesources.conf +# +# Syntax is: ;;; +# +# = Path, where to finde images or image directories +# = descriptor displayed in VDR +# = 0 - if no mounting should be done +# 1 - if needs to be mounted first. +# (Dont forget to setup fstab !!!) +# = filters, what files should be displayed. +# i.e. *.jpg *.jpeg *.png *.tif* *.bmp +# +/cdrom;CDROM;1;*.jpg *.jpeg *.png *.tif* *.bmp +/video;VDR Recordings;0;*.pnm +/mnt/images;Images on the network;0;*.jpg *.jpeg *.png *.tif* *.bmp +/mnt/camera;Digital Camera;1;*.jpg diff --git a/i18n.c b/i18n.c new file mode 100644 index 0000000..54951ad --- /dev/null +++ b/i18n.c @@ -0,0 +1,625 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold + * (C) 2003 Kai Tobias Burwieck + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include "i18n.h" + +const tI18nPhrase Phrases[] = { + { "Image", /*English*/ + "Bilder", /*Deutsch*/ + "", // TODO /*Slovenski*/ + "", // TODO /*Italiano*/ + "", // TODO /*Nederlands* + "", // TODO /*Português*/ + "", // TODO /*Français*/ + "", // TODO /*Norsk*/ + "Kuvat", /*suomi*/ + "", // TODO /*Polski*/ + "", // TODO /*Español*/ + "", // TODO /*Ellinika*/ + "", // TODO /*Svenska*/ + "", // TODO /*Romaneste*/ + "", // TODO /*Magyar*/ + "", // TODO /*Català*/ + }, + { "A Image Viewer plugin", /*English*/ + "Ein Bildbetrachter Plugin", /*Deutsch*/ + "", // TODO /*Slovenski*/ + "", // TODO /*Italiano*/ + "", // TODO /*Nederlands* + "", // TODO /*Português*/ + "", // TODO /*Français*/ + "", // TODO /*Norsk*/ + "Katso valokuvia ruudullasi", /*suomi*/ + "", // TODO /*Polski*/ + "", // TODO /*Español*/ + "", // TODO /*Ellinika*/ + "", // TODO /*Svenska*/ + "", // TODO /*Romaneste*/ + "", // TODO /*Magyar*/ + "", // TODO /*Català*/ + }, + { "Image browser", + "Bilder Verzeichnisanzeige", + "Image navigator", + "", // TODO + "", // TODO + "", // TODO + "Navigateur Images", + "", // TODO + "Kuvat - selain", + "", // TODO + "", // TODO + "Image endiksi fakelon", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Image source", + "Bilder-Datenträger", + "Image izvor", + "", // TODO + "", // TODO + "", // TODO + "Source Images", + "", // TODO + "Kuvat - lähteet", + "", // TODO + "", // TODO + "Pigi Image", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Parent", + "Zurück", + "Nazaj", + "", // TODO + "", // TODO + "", // TODO + "Parent", + "", // TODO + "Takaisin", + "", // TODO + "Predecesor", + "Piso", + "Tillbaka", + "", // TODO + "", // TODO + "Anterior", + }, + { "Select", + "Auswählen", + "Izberi", + "", // TODO + "", // TODO + "", // TODO + "Sélectionner", + "", // TODO + "Valitse", + "", // TODO + "Seleccionar", + "Epilogi", + "Välj", + "", // TODO + "", // TODO + "Escollir", + }, + { "Error scanning directory!", + "Fehler beim Lesen des Verzeichnisses!", + "Napaka pri pregledovanju direktorija!", + "", // TODO + "", // TODO + "", // TODO + "Erreur de parcours du répertoire!", + "", // TODO + "Hakemiston selaus epäonnistui!", + "", // TODO + "¡Error al leer una carpeta!", + "Lathos stin sarosi tou fakelou!", + "Kunde inte läsa katalogen!", + "", // TODO + "", // TODO + "Error al llegir una carpeta!", + }, + { "Source", + "Datenträger", + "Izvor", + "", // TODO + "", // TODO + "", // TODO + "Source", + "", // TODO + "Lähde", + "", // TODO + "Origen", + "Pigi", + "Källa", + "", // TODO + "", // TODO + "Orígen", + }, + { "Mount", + "Einbinden", + "Priklopi", + "", // TODO + "", // TODO + "", // TODO + "Monter", + "", // TODO + "Kiinnitä", + "", // TODO + "Mount", + "Sindesi", + "Montera", + "", // TODO + "", // TODO + "Mount", + }, + { "Unmount", + "Aushängen", + "Izklopi", + "", // TODO + "", // TODO + "", // TODO + "Démonter", + "", // TODO + "Irrota", + "", // TODO + "Unmount", + "Aposindesi", + "Avmontera", + "", // TODO + "", // TODO + "Unmount", + }, + { "Selected source is not mounted!", + "Ausgewählter Datenträger ist nicht eingebunden!", + "Izbran izvor ni prikljucen!", + "", // TODO + "", // TODO + "", // TODO + "Source sélectionnée non montée!", + "", // TODO + "Valittua lähdettä ei ole kiinnitetty!", + "", // TODO + "¡El origen deseado no està montado!", + "Epilegmeni Pigi den ine sindemeni!", + "Den valda källan är inte monterad!", + "", // TODO + "", // TODO + "l'Origen sel.leccionat no està muntat!", + }, + { "Mount succeeded", + "Einbinden erfolgreich", + "Priklop izveden", + "", // TODO + "", // TODO + "", // TODO + "Montage réussi", + "", // TODO + "Kiinnittäminen onnistui", + "", // TODO + "mount correcto", + "I sindesi petixe", + "Monteringen lyckades", + "", // TODO + "", // TODO + "mount correcte", + }, + { "Mount failed!", + "Einbinden fehlgeschlagen!", + "Napaka pri priklopu!", + "", // TODO + "", // TODO + "", // TODO + "Echec du montage!", + "", // TODO + "Kiinnittäminen epäonnistui!", + "", // TODO + "¡No he podido montar!", + "I sindesi apetixe!", + "Monteringen misslyckades!", + "", // TODO + "", // TODO + "No he pogut muntar!", + }, + { "Unmount succeeded", + "Aushängen erfolgreich", + "Izklop izveden", + "", // TODO + "", // TODO + "", // TODO + "Démontage réussi", + "", // TODO + "Irrottaminen onnistui", + "", // TODO + "Éxito al unmount", + "I aposindesi itan epitixisi", + "Avmonteringen lyckades", + "", // TODO + "", // TODO + "unmount amb èxit", + }, + { "Unmount failed!", + "Aushängen fehlgeschlagen!", + "Napaka pri izklopu!", + "", // TODO + "", // TODO + "", // TODO + "Echec du démontage!", + "", // TODO + "Irrottaminen epäonnistui!", + "", // TODO + "¡No puedo desmontar!", + "I aposindesi den itan epitixis!", + "Avmonteringen misslyckades!", + "", // TODO + "", // TODO + "No puc desmontar!", + }, + { "Eject failed!", + "Auswerfen fehlgeschlagen!", + "Napaka pri izmetu!", + "", // TODO + "", // TODO + "", // TODO + "Echec de l'éjection!", + "", // TODO + "Avaaminen epäonnistui!", + "", // TODO + "¡No puedo expulsar!", + "I apovoli apetixe!", + "Mata ut!", + "", // TODO + "", // TODO + "No puc expulsar!", + }, + { "Select picture via key 1..9!", + "Bildauswahl über Taste 1..9!", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Valitse kuva näppäimillä 1..9!", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Building SlideShow...", + "Erzeuge Diavorführung...", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Laaditaan diaesitystä..", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Scanning directory...", + "Durchsuche Verzeichnis...", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Scanne le repertoire...", + "", // TODO + "Selataan hakemistoa...", + "", // TODO + "Leyendo las carpetas..", + "", // TODO + "Söker igenom katalog...", + "", // TODO + "", // TODO + "Revisant les carpetes..", + }, + { "No Files!", + "Keine Dateien!", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Ei tiedostoja!", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Error building SlideShow!", + "Fehler beim Erzeugen der Diavorführung!", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Diaesityksen laatiminen epäonnistui!", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "SlideShow ?", + "Diavorführung ?", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Käytä diaesitystä", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Slide duration (sec)", + "Anzeigedauer (Sek)", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Dian esitysaika (s)", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Directory with temporary files", + "Verzeichnis mit temporären Dateien", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Väliaikaistiedostot", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Show Filedate on OSD", + "Zeige Dateidatum im OSD", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Näytä päivämäärä näytöllä", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Repeat SlideShow", + "Wiederholung der Diavorführung", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Diaesityksen uudelleentoisto", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Show Numbers on index image", + "Zeige Zahlen im Indexbild", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Näytä numerot indeksikuvassa", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, +#if VDRVERSNUM >= 10308 + { "Live Audio from primary Device", + "Live Audio vom primären Gerät", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Live-ääni päävastaanottimelta", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, +#endif + { "Convert...", + "Wandle...", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Konvertoi...", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Zoom", + "Vergrößere", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Suurenna", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Execute", + "Ausführen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Suorita", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Operation failed", /*English*/ + "Vorgang fehlgeschlagen", /*Deutsch*/ + "", // TODO /*Slovenski*/ + "", // TODO /*Italiano*/ + "", // TODO /*Nederlands* + "", // TODO /*Português*/ + "", // TODO /*Français*/ + "", // TODO /*Norsk*/ + "Toiminto epäonnistui", /*suomi*/ + "", // TODO /*Polski*/ + "", // TODO /*Español*/ + "", // TODO /*Ellinika*/ + "", // TODO /*Svenska*/ + "", // TODO /*Romaneste*/ + "", // TODO /*Magyar*/ + "", // TODO /*Català*/ + }, + { "Image couldn't load", /*English*/ + "Konnte Bild nicht lesen", /*Deutsch*/ + "", // TODO /*Slovenski*/ + "", // TODO /*Italiano*/ + "", // TODO /*Nederlands* + "", // TODO /*Português*/ + "", // TODO /*Français*/ + "", // TODO /*Norsk*/ + "Kuvan lukeminen epäonnistui", /*suomi*/ + "", // TODO /*Polski*/ + "", // TODO /*Español*/ + "", // TODO /*Ellinika*/ + "", // TODO /*Svenska*/ + "", // TODO /*Romaneste*/ + "", // TODO /*Magyar*/ + "", // TODO /*Català*/ + }, + { "Script execution failed", /*English*/ + "Scriptausführung fehlgeschlagen", /*Deutsch*/ + "", // TODO /*Slovenski*/ + "", // TODO /*Italiano*/ + "", // TODO /*Nederlands* + "", // TODO /*Português*/ + "", // TODO /*Français*/ + "", // TODO /*Norsk*/ + "Skriptin suoritus epäonnistui", /*suomi*/ + "", // TODO /*Polski*/ + "", // TODO /*Español*/ + "", // TODO /*Ellinika*/ + "", // TODO /*Svenska*/ + "", // TODO /*Romaneste*/ + "", // TODO /*Magyar*/ + "", // TODO /*Català*/ + }, + { "Remove temporary files", /*English*/ + "Lösche temporäre Dateien", /*Deutsch*/ + "", // TODO /*Slovenski*/ + "", // TODO /*Italiano*/ + "", // TODO /*Nederlands* + "", // TODO /*Português*/ + "", // TODO /*Français*/ + "", // TODO /*Norsk*/ + "Tyhjennä väliaikaistiedostot", /*suomi*/ + "", // TODO /*Polski*/ + "", // TODO /*Español*/ + "", // TODO /*Ellinika*/ + "", // TODO /*Svenska*/ + "", // TODO /*Romaneste*/ + "", // TODO /*Magyar*/ + "", // TODO /*Català*/ + }, + { NULL } +}; diff --git a/i18n.h b/i18n.h new file mode 100644 index 0000000..936d2cf --- /dev/null +++ b/i18n.h @@ -0,0 +1,30 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold + * (C) 2003 Kai Tobias Burwieck + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef ___I18N_H +#define ___I18N_H + +#include + +extern const tI18nPhrase Phrases[]; + +#endif //___I18N_H diff --git a/image.c b/image.c new file mode 100644 index 0000000..fae80ca --- /dev/null +++ b/image.c @@ -0,0 +1,129 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2003 Kai Tobias Burwieck + * (C) 2004 "Interpohl" + * (C) 2004 A. Brachold + * (C) 2004 O. Kreuzinger + * (C) 2004 A. Holzhammer for the massive script updates + * + * based on mp3/mplayer plguin by Stefan Hülswitt + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include + + +#include "setup-image.h" +#include "data-image.h" +#include "menu-image.h" +#include "i18n.h" +#include "commands.h" +#include "liboutput/encode.h" + +static const char *VERSION = "0.2.3"; +static const char *DESCRIPTION = "A Image Viewer plugin"; +static const char *MAINMENUENTRY = "Image"; + +class cPluginImage : public cPlugin { +public: + virtual ~cPluginImage(); + virtual const char *Version(void) { return VERSION; } + virtual const char *Description(void) { return tr(DESCRIPTION); } + virtual const char *CommandLineHelp(void); + virtual bool ProcessArgs(int argc, char *argv[]); + virtual bool Start(void); + virtual const char *MainMenuEntry(void) { return tr(MAINMENUENTRY); } + virtual cOsdMenu *MainMenuAction(void); + virtual cMenuSetupPage *SetupMenu(void); + virtual bool SetupParse(const char *Name, const char *Value); + }; + +bool cPluginImage::SetupParse(const char *szName, const char *szValue) +{ + return ImageSetup.SetupParse(szName,szValue); +} + +const char *cPluginImage::CommandLineHelp(void) +{ + static char *help_str=0; + + free(help_str); // for easier orientation, this is column 80| + asprintf(&help_str," -m CMD, --mount=CMD use CMD to mount/unmount/eject image sources\n" + " (default: %s)\n" + " -C CMD, --convert=CMD use CMD when converting Images\n" + " (default: %s)\n", + g_szMountScript, + g_szConvertScript + ); + return help_str; +} + +bool cPluginImage::ProcessArgs(int argc, char *argv[]) +{ + static struct option long_options[] = { + { "mount", required_argument, NULL, 'm' }, + { "convert", required_argument, NULL, 'C' }, + { NULL } + }; + + int c, option_index = 0; + while((c=getopt_long(argc,argv,"m:C:",long_options,&option_index))!=-1) { + switch (c) { + case 'm': g_szMountScript=optarg;break; + case 'C': g_szConvertScript=optarg;break; + default: return false; + } + } + return true; +} + +bool cPluginImage::Start(void) +{ + if(!cEncode::Register()) { + return false; + } + + ImageSources.Load(AddDirectory(ConfigDirectory(), "imagesources.conf")); + if(ImageSources.Count()<1) { + esyslog("imageplugin: you must have defined at least one source in imagesources.conf"); + return false; + } + + RegisterI18n(Phrases); + return true; +} + +cPluginImage::~cPluginImage() +{ + cEncode::UnRegister(); +} + + +cOsdMenu *cPluginImage::MainMenuAction(void) +{ + return new cMenuImageBrowse; +} + +cMenuSetupPage *cPluginImage::SetupMenu(void) +{ + return new cMenuSetupImage; +} + + +VDRPLUGINCREATOR(cPluginImage); // Don't touch this! diff --git a/image.h b/image.h new file mode 100644 index 0000000..84e0c5c --- /dev/null +++ b/image.h @@ -0,0 +1,67 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef ___IMAGE_H +#define ___IMAGE_H + +#include +#if VDRVERSNUM < 10307 +#include +#else +#include +#include +#endif +#include + +inline void OSD_InfoMsg(const char* sz) +{ +#if VDRVERSNUM < 10307 + Interface->Info(sz); + Interface->Flush(); +#else + Skins.Message(mtInfo,sz); + Skins.Flush(); +#endif +} + +inline void OSD_ErrorMsg(const char* sz) +{ +#if VDRVERSNUM < 10307 + Interface->Error(sz); + Interface->Flush(); +#else + Skins.Message(mtError,sz); + Skins.Flush(); +#endif +} + +inline void OSD_ErrorNumMsg(int err, const char* szDef) +{ + char szErr[128]; + int nErr = err; + szErr[sizeof(szErr)-1] = '\0'; + if(0 != strerror_r(nErr,szErr,sizeof(szErr)-1)) { + szErr[0] = '\0'; + } + OSD_ErrorMsg(szErr[0] != '\0'?szErr:szDef); +} + +#endif //___IMAGE_H diff --git a/libimage/Makefile b/libimage/Makefile new file mode 100644 index 0000000..e46bb0c --- /dev/null +++ b/libimage/Makefile @@ -0,0 +1,51 @@ +# +# Makefile for a Video Disk Recorder plugin +# +# $Id$ + +VDRDIR = ../../../.. + +### The C++ compiler and options: + +CXX ?= g++ +CXXFLAGS ?= -O0 -g -Wall -Woverloaded-virtual + +-include $(VDRDIR)/Make.config + +### The directory environment: + + +INCLUDES += -I$(VDRDIR)/include + +DEFINES += -D_GNU_SOURCE + +LIBS += + +### The object files (add further files here): + +OBJS = pnm.o xpm.o + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< + +# Dependencies: + +MAKEDEP = $(CXX) -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Targets: + +all: libimage.a + +libimage.a : $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) +# $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@ + +clean: + @-rm -f $(OBJS) $(DEPFILE) *.a *.so *.tgz core* *~ diff --git a/libimage/error.svg b/libimage/error.svg new file mode 100644 index 0000000..b633739 --- /dev/null +++ b/libimage/error.svg @@ -0,0 +1,2167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libimage/error.xpm b/libimage/error.xpm new file mode 100644 index 0000000..f704a56 --- /dev/null +++ b/libimage/error.xpm @@ -0,0 +1,454 @@ +/* XPM */ +static char * error_xpm[] = { +"491 386 65 1", +" c None", +". c #5E605D", +"+ c #525350", +"@ c #454744", +"# c #717271", +"$ c #45586D", +"% c #8F908F", +"& c #9C9DA7", +"* c #CFD0DA", +"= c #C6C6C6", +"- c #AAAAAA", +"; c #FBABA0", +"> c #2F2F2C", +", c #F5F2F6", +"' c #FDFFFC", +") c #985053", +"! c #E9E7EB", +"~ c #DDDEE8", +"{ c #B1DFFE", +"] c #D8D9D8", +"^ c #0F1929", +"/ c #000100", +"( c #1A1B17", +"_ c #0C2154", +": c #266BF5", +"< c #2F79F6", +"[ c #3A8CFA", +"} c #4B78D3", +"| c #4BA1FD", +"1 c #3D6894", +"2 c #2E619E", +"3 c #5A90D1", +"4 c #135AF2", +"5 c #084BF1", +"6 c #003AEF", +"7 c #62B3FF", +"8 c #6490BD", +"9 c #84B1DB", +"0 c #9B0400", +"a c #C12625", +"b c #96292D", +"c c #C44842", +"d c #FC5F52", +"e c #DC7C7A", +"f c #FC9687", +"g c #FB8474", +"h c #FC705E", +"i c #80C6FF", +"j c #D5302D", +"k c #215EB9", +"l c #EF9C92", +"m c #8FADC7", +"n c #0A2B88", +"o c #EA3C38", +"p c #FC544A", +"q c #97BEDB", +"r c #9AD3FF", +"s c #5B5712", +"t c #736B15", +"u c #49460E", +"v c #FC4C45", +"w c #F4E52D", +"x c #A49A1B", +"y c #F93D39", +"z c #FD2B2C", +" .......................................+..+..+.+.+.+.++.++.++.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@+++@++@++@++@+@+@+@+@@+@@+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ", +" ..................+...+...+.+.+.++.+++.+.+.+..+.+.+.++.+.++.++.++..+.+.++.+.++.++++++++++++++++++++++++++++++++++++++@++@++@+@+@+@+@+++@++@++@+@+@+@+@+@+@+@@+@@+++@++@++@+@+@+@+@@+@@@+@@@@@@@@@@@ ", +" ......................+...+........+...+...+.++...+.++.+.++.++.++.++++.++.++++.+++.++.+.++++++++++++++++++++++++@++@+++++++@++++++++++@+@+@++@+@+++@+@+@+@+@++@+@+@@@+@@@@@@@@@@@@@@@@@+@@@@@@@@@@@@@@ ", +" ....#$..$..$..$+.$..$+.$..$..$+.$+.$..$..$+.$..$++$.+$.+$+.$+.$+.$++$.+$++$.+$++$++$++$++$++$++$++$++$++$++$++$++$++$++$+@$++$+@$+@$@@$+@$+@$+@$+@$+@$+@$@+@$@@$@@$@@$@@$+@$+@$+@$+@$@@$@@+@$+@$@@$@@@@@ ", +" ......#+#............#+.............+.+.+...+.+...+.+.+..+.++.++.+.+++.+.++++.+++.+++++++++++++++++++++++++++++@++@++@++++@++@++@+++++@++@++@+@@@+@+@@@@@@$@@@@+@@+@+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ", +" .#$.......+#+#+#+#+....+#+.+..+.+.+.+.......+....+..+.+.++.+.++.++.++.+++++++++.++++++++++++++++++++++++++++@++++++++++++@+++++@++@+@+++@+++@+++++@+@++@++@+@++@+@+@@@@@+@@+@+@@@@@@@+@@@@$@@@@@@@@@@@@@@@@ ", +" ....#+#+#+#+.$.+.$..+$#+.$#+#$..$.#$.+$.+$+#$++$.+$.+$+.$+.$+.$+.$+.$+.$.+$.+$++$+.$++$.+$++$++$++$++$++$++$++$++$++$++$++$@+$++$@+$+@$++@+$@+$@@$@++$@+@@+@+@@$@@$@+$@@$@@$@@$@+$@@$@@@$@@@@@$@@@$@@@$@@@@ ", +" +#+#+.$+%&************=***=*=*=**==*=**=***=*=**=**=**=**=**=**=**=*==*==**=**=**=*==*==**=**=*==**==*============================*===*-**==*===*==**==*****=**=====*==**==*;**=-=====**==-**===*==%$@@@@@@@> ", +" ....#.##,,,,,'',,,,,,',,,,,,,,,,,,,,',,',,,,,',,',,',,'',,,'',','',',,'',''','',',,''''','''''''',',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',,,,,,,,,,,,,,,,,,,,,,,-@@@@@@@> ", +" .#$+.++&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',''''''',,,''''''''''''',,,,,,''''',,,,,',,,+>$@@@@> ", +" ...#.#+-'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',@@@@@$@> ", +" ..$)...-'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',@@@@@@@> ", +" ...$#+.-''''''',',,,,,,'',',',',',,',,',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,!,,,,,,,!,,!,,!,!,!,!,!,!,!!,!!,!!,!!,!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!''''''@@@@@@@> ", +" #......-''''',,','''''',,',',',,,'','',,',',''',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,!,,!,,!,!,!,!,!!!!!!!!!!,!!!!!!!!!!,!!!!,!!!,!!!!!,!!!,~,!!!,~,!,!!,!!,!!,!!,!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!,'''',$>$@@@@> ", +" ....#+.&'''''',',,,,,',',,,',',,,,,,,,,,,,,,,,,''''''''''''''''!''!''!'!'!'!'!,!,!'!'!'!,!'!'!'!,,,'!','!,!,,!,!,!,!,!!,,!!,!,!!,!,~!,!,!!!~,~~,!~~~,~~,~~,~~,~~,~,~,~,~,~,~,~,~!~~~~~~~~~~,~!!''''',+@@@@$@> ", +" +#$....-''''''',''''',',',',',,',','''''',''',,!,!!,!!,!!!!,!!,,!',,!'!'!,!'!'!'!'!,!'!,!,!,!,!,!!!!!!!!,!,!,!,!!,!,!,~,~!,!!,~,~,~,~!,~,~,,~,~,~,,,~,~,~,~,~,~,~~,~~,~~!~!~!~!~!,,,,,,,,~!~!~!,''',,@@@@@@@> ", +" ...#+#+-'''',,',,,,',,,,',,,',',','!!,!,,'!,!',,',,,',,',',,',,,,!,,,,!,!'!,,,!'!!,,!,,'!,!'!!'~',,'!,!,!!'~'~'~'~!'~,!,,~'~'~,,,~,,,~,!~,~,,~,~,~~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~~~~~~~~~,~,~!!''''',@@@@@@@> ", +" ...$...-'''''',,''','',',',',,',,!',',',,,'!','~',,,,,,,,,,,!'!'!'!'!',',,,'!!'!,!'!'!!!'!!'~'~,!!!!!'~'~'~,!~'~!,!~'~,~'~,~,~,~,~,~!,~,~,~,~,~,~,~,~,~,~,~,~~,~,~,~,~,~,~,~,~!~!!!!!!,,~,~!~!~''''',>$@@@@@> ", +" .#+#...&''''''',,',,,',,'',,,','','!,,,,,',,,,,,,',,',,'~',,,,,,',~'!~,~'!,!'!,'!,!~'~'!!'!~,,~'~''!'~~'~,~'~'~'!~'~,~'~,~'~'~,~,,~'~,~,,~,~,~,~,~,~,~,~,~,~,!~!~,~!~,~,~,~!~,~,~~,~~~~~!~,~,~!''''''@@@@@$@> ", +" .......-''''',,'','',',',,,',',,,,,',''!',!'!',',,,'~,,',',,'~'~,',,'','!'~'~'~'!','~,!'~'~','~'!~'~,,,~'!,~'~,~'~'~,~'~'~,~,~,~,~,~,~,~,~,~,,~,~,~,~,~,~,~,~,~,~!~,~,~!~,~,~!~!,~~,!,!,~!~!!~!,'''',@@@@@@@> ", +" ...$#+.-'''''',',,,,',',','!,,',,',,~',,'~',,,~','~'',,,~'~',,,',~'~,~'~','~',~'~,~''~'~'~'~~'~'~,~'~'~,~'~,~'~'~,~'~,~'~'~'~,,,~'~,~,~,~,~,~~,~,~,~,~,~,~!~,~,~,~,~!~,~,~!~,~,~~,!~~~~~,~!~!!!''''',@@$>@@@> ", +" +#.....-''''',','',',',,',,','!',,,''!',''~',''~,,'~',',','~'~',,,,,',,,,~,'~''~'!'~'~,'~'~'~'!,'~'~,~'~',~'~'~~'~,~'~'~,~~,~,~,~,~,~,~,~,,~,~,~,~,~,~,~,~,~!~,~,~,~,~,~!~,~!~!,~!~,!,,~!~,~~~!,''',,@@@$@@+> ", +" ...#+..&''''',,,,',,!~~~~~~~~~~~~~~~~~~*~~~~~~~{~~~~~{~~~{~~~~~{~~~{~~~~{]~~~~~~~~~~~~~~~~~~~~~*~~~~~~{~~{~~*~~{~~~~~~~~~~~~~{~~~~~{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~**~~~!~,!!!''''',@@@@@@@> ", +" #+.+#..-'''''''',','~*************{*****{***{****{*****************************{********{*************************{********************{****{*************{*************************~***~,~~~!~''''',@@@@@@@> ", +" .$#+#+.-''''',,',','~~*{***{*{******{**********************{************~*{******{**{*****{***{**{*********{********{**{*{**~***{**~*****{*********{**************{************~*****{*~~!~,~!~''''',@@@@@$@> ", +" .)$....-'''''',,',,'~*************************~***************~**~***********************************~******~**~**************~****************{*************{*******{**{***~***********~,~!!!~''''',@$@@@@@> ", +" ...#...&''''',',',',~~***~****~****~*~*~*~**~***~*~*{*~*~*~*~*****~*{*~****~******~**~**~**~*~*~**~***~*~****~***~**~*~*~*~****~*~**~*****~**~**~**~*{*~*~*~**~*~**~**~**~*{**{**{*~**~*~!!~~,~''''',@>@@@@@> ", +" #...$..-'''''',',,,'~*~****~****~*************~****~**********~*{***~***~***~*~*~********~******~***~*****~****~************~*****~**~*~***~********~************~**********~***~***~**~~~!!!~~''''',@$@@@@@> ", +" ...#+#+-''''',,,',',,**~**&^>^>^>^>^>^>^>^>^>^>^>^^>^>^>^>^>^>^>>^>^>^>^>^>^^>^^>^>^>^>^^>^>^>^^>^>^>^^>^>^>^^>^>^>^>^>^>^>^>^>^>^>^^>^>^>^>^>^>^^>^^>^>^>^>^>^^>^>^>^>^>^>^^>^>^>~**~**~,~~!!~''''',@@@@@$@> ", +" #+.....-''','',,,,,,~~****&////////////^/////////////////////////////////////////////////////////////(/////(///////////////////////////////////(////////^////(///////////////////^****~~~,!!~,~''''',@@@@@@@> ", +" ..#+#+.-,'''',',',',,*{***&///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////~*****~~~,~~~''''',@@@@@@@> ", +" ...$...-''''',,,,',,~~****#//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////^**{~*~,~!~!!!'''',,$>$@@@@> ", +" ..#.#+.-,''''',',,,,~~*~*~&///_:<:<<<<<<<<<[<[[[[[[[[[[[[[[[[}|[}[[[|[|[|}|[||}|[|}11232[|[|[}|[[[[[[[[[[[<[[<[<[<<<<<<<<:<::::::::4:44444454545555556556666666666666666666666////~****~~!~,~~~''''''@@@@@$@> ", +" #+.+...-'''''',,',',~*****%///_<<<<<[<[[[[[[[[[[[[[[[|[|}||||[||||||||||||||||||1++++@+++1[|||[|||[|[}[[[[[}[[[[}<[[[<<<<<<<<<<:::::::::44444444455555565566666666666666666666///^**~*~*,~~~!~!,''',,@@@@@@@> ", +" ..#$#..-,'''',',,,,,,~****&///_<<<<<[<[<[[[[[[[[[[|}[|[|[|[|||||||||||||||||||3@@@@@@@@@@+$2|}|}|[||[|[|[|[[[[[[[[<[[<[<<<<<<<:<::::::4:4:444445455555556666666666666666666666////~***~*~~,!~!!''''',@@@@@@@> ", +" #+.....-''''',,'',,,~**~**%//(_<<[[<[<[[[[[[[[}[|[|[|[||||||[|||||||||||||||3$@@$@@@@@@@@@@@13}3}|[||[||[[|[[[[[[[[[[[<[[[<<<<<<<<:::::::4:44444445555565556666666666666666666///^*~*~*~!!~!!~!,''',,@@@@@@@> ", +" ..#+.$.-''''',,,',,,,~****&///_[<[<[[[[[[[[[[|[|[|||||[||[|||||||||||||7||3$@@@@@@@@@@@@$@@@@22323}|||[||[|[|[[[[[[<[[[<<<<<<<<:<::<:::::4:44444454555556566666666666666666666////*~***~~~~!~!!''''',@@$>$@+> ", +" $.+#...-''''',',,',,~**~**%//(_<[<[<[[[[[}[[|[[|[|[|[|||||||||||7||77|7|71+@+@@@@@@$@@@@@@@@@@122222}|||||[||[|[[[[[[[[[[[[[<<<<<<<:::::::4::444444445555555666666666666666666///^**~~~*!!~!!~!,''',,@@@@@@@> ", +" .#+#+..&''''',,,,,,,,~*~**#///_[[[[[[[[[[|[|[|||[||||||||7|7|7||7|7|7|78@@@@@@@@@@@@@@@@@@@@@@$211213}|[||[||[|[|[}[[[[[[<<[<[<<<<<<:<:::::4:444445454555566666666666666666666////~***~~~~,~~!!''''',@@@@@@$> ", +" #+#+#..-''''',',',,,~*~*~~%//(_[<[[[[[[[[[|[|[||||||||7|||7||7|777|778$@@+@$@@@$@@@@+$@@@@$>$@>12122223||||||[||[|[|[[[[[[[[[<[<<<<<<<:::::::::4444544555555566666666666666666///^*~*~*~!~~,~~!,''',,@@@@@@@> ", +" ...+#+.-''''',,,,,,,,~****&///_[[[[[}|[||||||||||||||||77|77777|7773$@@@@@@@@@@@@@$*,,&+@@@@@@@$11112233|||||||[|[|[[}[[[[[<[<[<<<<<<<<:::::444:444445455555656666666666666666////~*~**~~~~!~!!''''',@@@@@@@> ", +" +#$....-''''',','',,~*~~**%//(_[[[[[[[[|[[|[|||||||7|7|7|7|||777771@+@@+@@$@@+>$+-,',,,&@@@@@@>>1213112}3|||||||||[|[|[[[[[[[[<[[[<<<<<<<:::::::4:4444445555556666666666666666///^*~*~~~!!!!~~!,'''',>$@@@@@> ", +" ...#+#+&''''',,,,',,,~*~*~%///_|[[|[||[||||||||||7|7|7|777777|738@@@@@@@@@@@@$)%,,'''',,+@@@@@@@$111211333|||||[||||[|[|[[[[[[[<<<<<<<<:<::::::4:44444545455655666666666666666////~~***~~~~~!!!''''''@@@@@$@> ", +" #..$...-''''',',,,,,~*~~**%//(_[|[[|[|[||[||||7|7|7|7777|777778$@@@$+@$@+@@@@#!,'''''',,-$>$@@@@>$11321233||||||||[|||[|[|[[[[[[[[<[<<<<<<<:::::::4444444555555666666666666666///^**~~~~~,,~~~!,'''',@@@@@@@> ", +" ..#)$#+-''''',',,,,,,~*~*~%///$[[|[||||||||||||77|77|77777778$@@@@@@@@@@@@@#*,'''''''''',#@@@@@@@$1111213337|7||||||[|[|[[[}[[[[<[[<<<<<<<:<::::4:4:44454455555566666666666666////~~**~~~~~!!~!''''',@@@@@@@> ", +" .+.....-''''',,',,,,~~~*~*%///_[|[|[|[||||7|77|7|77777777731+@+@+@+@@@@$@+*,'''''''''''''*@$>$@@>>$11821233|7|7|||||||||||[|[[[[[<[[[[<<<<<<::::::4:44444445555556666666666666///^~*~~*~!!~!~!~'''',,$>$@@@@> ", +" .......-''''',,,',,,~~~*~*&///$[[|||||||7|||77|77|77777771++@@@@@@$@@@@)&,,''''''',,''''',%@@@@@@@>111111333777|7||||||[|[|[|[[[[[[<[<<<<<<:<::::::4:4444545555556666666666666////~*~~*~~~!~,~~''''',@@@@@$@> ", +" ..#$...-,'''''~,,,,,~~~~**%///_|||[|||||||77|77777777778++@@++@$+>)@$@%~,,'''''',~!!,'''',!+@@@@@@>$11311133||7|77||||||||[|[|[[[[[[[[[[<<<<<<<:::::4:444445455555666666666666///^~~*~~~~,~~~!~''''',@@@@@@@> ", +" .......-''''',',,,,,,~*~~*%///$[||||||7|77|7|77|777778$@@@@@@@@@@$>@#*,''''''',!!~!!,''''',-@@$@@@@>$111111337777|7||||||||[|[|[[}[[<[<<<<<<<:<:::::4:444444555555566666666666////*~~*~~~~,~,~~''''''@@@@@@@> ", +" #.+#...-''''''~',,,,~~~*~*%///_|||||7||77|7777777773$@@@++@$+@+@@+.*,'''''''',!!!!!~~,'''',,.@>$@@@$>111311333|7|7777|||||||||||[[[[[[[[[[<<<<:<::::::4:4444445555566666666666///^~*~~*~,~~~~!~'''',,@@@@@@@> ", +" ...$...&''''',',',,,,~~~~~%///$[|||||7|77777777777$.++@@@@@@@@$@+-,''''''',,~!!~!~!!!!''''''*@@@@@@@@$11118133777|7|7|7|||||[|[[|[[[[[[<<[<<<<<<:<:::4:4:445454555556666666666////~~~~~*~~,~~,~''''',@@$>$@@> ", +" +#.#...-''''''~',,,,~~*~**%///_||||7|7777|7|77778++@+@++$@+@@@+&,,''''''',!~!~,~,~!!!~,''''',%@@@@@@@>111111833777777|7|||||||||[|[[[[[[<}<[<<<<<::::::44444454555555666666666////~~*~~~,~~!~~!,''',,@@@@@@+> ", +" .+...$.-''''',,,,,,,,~~~~~%///$|7|7|7|7|7777778+@@+$@@@@@+@$@#!,''''''',!!~!!!~!~,~~!~!''''''!+@$>@@$@$1181113377|7|7|7|7|||||[||[|[}[[[[[<<<<<<:<<::::::4:4445455556566666666////~~~~~~~~,~,~~''''',@@@@@@@> ", +" #+#....-'''''',,',,,~~~~**%///_|||77|77777779$@++@@@@$+@+@@#*,''''''',,!~!,~~,~,~!!~,~!!''''',&@@@@@@>@$111811337777777|7||||||||[||[[[[<[[[[<<<<:<::4:::444445455555566666666///^~~*~~~,~~!~!!'''',,@@@@@@@> ", +" 0000000000000000 .......&''''',,'~',,,~~~~~&///$77|77|777777$++@@$@+@@@@+@)-,'''''''',~!!!~~,!~!~,~!!~!!~,''''',.@@@@@@@$$11818833777777777|7||[|[|[|[|[[[[[<<<<<<<<:<:::4:4:444545555656666666////*~~~*~~~!!~~~''''',@@@@$@@> ", +" 0000000000000000000000000000000000000 #$#+#..-''''',,',,,,~*~~~*%///$|7|77777778++@@++@+@++@@$&,'''''''',!~!,~!,~~,~,~,~~!~!!!!''''',*$@@@@@@@$11111138777777|7|7||7||||[|[|[[[[[[[[}<<<<<::::4::4444445555556666666////~~~~~~,~~!!!!,''',,@@$>@@@> ", +" 00000000000000000000000000000000000000000000000000 .+#+...-''''''~',,,,,~~*~~%///$||77|7778@@@+$@@+@+@+@$%!'''''''',~,~!!~,~~,!~!~!~,!~,~~!~,''''''#@@@$@@@@11181883377777777|7|||||||[|[|[[[[[<<<<<<<:<:::::4:444445455556566666////~~~~~~~!!~~!~''''',@@@@@@$> ", +" 000000000000000000000000000000000000000000000000000000000000 ..+#...-''''',,,,,,,,~~~~*%///_7777778$@+++@+@$+@@$@#!,''''''',,!!~,~,~!,~~,~,~,~~,~!!!!!!''''',!$@>@@@@@$$181118877777777|77|7|||||[|[[[[[[[[<}<<<<<<:::::4:44454555556666666////~~~*~~~~!!~!~'''',,@@@@@@@> ", +" 00000000000000000000000000000000000000000000000000000000000000000000 .#+$...&'''''',,,,,,~~~*~~%///$|7|73$+++@+@+@@@@+@)*,,'''''''!~!!!!~!~,~~,!~!~,~,~~!~~~~!~,''''''%@@@@$>@@111818838777777777|7|||||||[|[|[[[[[<<<<<<<:<::::4:44444545555566666////~~~~~~!!~!!~~''''',@@@@@@@> ", +" 0000000000000000000000000000000000000000000000000000000000000000000000000000 ...#...-'''''~'',,,,,~~~~*%///$7778++++@+@$@++@$+&,,''''''',!!,~,~,~,~!,~~,~,~!~**~!!,!!!~!,''''''$@@@@@@@@$1811183377777|77|777||||||[|}[[[[[[[}<<<<:<:::::4:4444455555565666////~~~~~~~!~~!~!,''',,@$@@@@+> ", +" 000000000000000000000000000a0aabaccccccccdcacaaa0000000000000000000000000000000000 ..+#+#+-''''',,,',,,~~~~~~%///_78@@+@$@++@+@+@+&,'''''''',~!!~!!~,~,~,~~,!~!~,~*~**~~~~~,~!!''''''=@@@@@@@@$118188837777777777||7|||||[|[|[[[[<[<<<<<<:<:::4:44445454555566666////~~~~~~!~,~!~!''''',@@@@@@@> ", +" 0000000000000000000000acccefegeefgggggggggggghghghghhghchdcaa000000000000000000000000000 #+#+...-'''''',,,,,,,~~~~*%///$$@++@++@$@++++#!,''''''',!!!,~,~,~!,~~!,~!~,~~******~~!!!~!!~,''''',#@@$@@@@@$$18118897i77777|77|7|||||||[|[[[[[[[<}<<<:<:::::::444445555555566////~~~~~~~!~~~!~,'''',>$>$@@@> ", +" 000000000000000000abcceefegffggfggggggggghghghhghghhhhhhghhghhhhghhcjaa00000000000000000000000 ..$#+#+-,'''',,,,,,,~~~~~~%//@@+@++@++@+@+@#*,,'''''',,!,~~~!~,~,~~,~!~,~!~~**~*****~~~~!~!!~''''''~@@@@@@@$$1181813877i7777777|7|||||[||[|[[[[<[[<<<<<<:::::444:5445455656666////~~~~~~!~~!~~!''''''@@@@@@$> ", +" 000000000000000000ceefffffffffgfgfgfgggggggggggghghghhghghhhhhhhhghhhhhghhhdcaa00000000000000000000 #......-'''''~',,,,,,~~~~~%>@+@++@$++@++@#&,,''''''',!!~!,,~,~,~!,~,~,~~~~**{****{*~~~,~!!!~!,''''',&@>$@@@@@$$11818887777777777777|||||[|[|[[[[<[<}<<<:k:::::4:44444555555556////~~~~~~~~,~~!~''''',+@@@@@@> ", +" 0000000000000000ceefffffffffgfffgfgfgggggggggghghghghghhhhhhghhhghhhhhghhhhhghhhhdcaa0000000000000000000 ...$#+.-,'''',~',,,,~~~~*&++++@++@+@+$@+&,,''''''',~!!~,~~,~,~~,~~,~,~,~*~*****~*****~~!~~!~~!,''''',+@@@@@@@$$1818138i7i777777|7|7|7||||[|[[|[[[[<<<<<<::<:::4:44445455556666////~~~~~~~!~~,~~''''',@@@@@@@> ", +" 000000000000000ceelfflfffffffffffgfgfgfggfgggggghgghghghhghghhhhghhghhhhhhhhghhhhghhhhhhdcaa00000000000000000 #$)....-'''''''~,,,,,~~&.@++@++$@+++@+%,,''''''',!!,~,~,,~!~!,~!,~,!~~*******~***~*~**~!!!!~!~!''''',=@@@@@@@@$$18188387i7i777777|7||||||[||[[[[[[[<}<<<:<:::::4:4445445555556////~~~~~~~,~~~!~'''',,@$@@@@@> ", +" 00000000000000celfffflfffffffefffffgfgfgfggggggghggghgghghhhhhhhhhhhhhhghghhghhhhhhhhghhhhhhhddca00000000000000000 ....#+.-,'''',,'','!,*#++$@++$@+@+@+#*,,'''''',,!!~,!,~,~,~,~,~,~!~~~**~***~***8&~**{~*~~~,~!!~,''''',#@$>@@@@$$$81188837777777777|77|||||[|[|[[[[<[<<<<<<:::::4:4444555555656///^~~~~~~!~~,~~~''''',@>@@@$@> ", +" 0000000000000bellflll;f;fllffffffgfgffgfggggggggggghghghghhhhghghhghhhhhhhhhhhhhhhghghhhhhghhhhhhdddaa0000000000000000 ...$...-'''''~'~,~!~#+@+@++@+@+++$+*,'''''''',!~,~,~~~,~~,~,~!~,~!~{***{*~**~&>/^*****~~,!~~!~!!''''''*@@@$@@@@$1$8118887i7i777777|7|7|||||[|[[[[[[[[[<<<<<:::::4:444445555556////~~~~~~~~~~~!~''''',@$@@@@@> ", +" 000000000000bcl;f;f;fff;fffffffffffefgfgggfgfggggghghghghhghghhhhhhhhhghhghhghhghghhhhhhhghhhhghhhghhhhdda0000000000000000 ...#...-,'''','',~&++$++++++++@++&,,''''''',!!!,~,~,,!,~,~,~,~,~~**********m$////&**~***~!~,~~!~,''''',%@@@@@@@@$1$881888i7777777777|7|||||[|[|[[[[<<<<<<<:<:::4:4444445555656////,~!~~~,~~,~!~''''',@@@@@@@> ", +" 00000000000bcf;;;;;f;;;fflfff;effffgfgeggefggggggghgghgghgghhhghhhhghghhghhhhhghhhhhhhhhhhhhhhhhhghhhhghhghddda000000000000000 #......-''''',,~-.++@++@$+@$@++&,'''''''',,~,~,~,~,~,~,~,~,~,~~****~**~***.//////^***~*~~!~~!!~!]'''''',+@@@@@@@.1$1818387i7i77777|77|7|||||[|[[[[[[[<}<<<:<::::::444445555565////~~~,~~~~!~~~!,'''',@@@@@@@> ", +" 00000000000bg;;;;f;f;f;ff;ffffffffffffgfgfgggggggggggggghhghhghhhhgchhhhhhhhghchhhhghghghghghhghhhhhhghhhhhhhhhdddca00000000000000 ..$#+..-,'''',=#++@+++@+@++++#~,,'''''',!!!!~,~,~~,~,~,~,~!~~~*{********1^////////$~****~~!~!~!~~,''''',-@$@@@@@@$1$8188887i77i77777|7|7|||||[|[[[[[[<<<<<<:<:::4:444444555556///(~~~~~~,~!~!~!''''',@@@@$@@> ", +" 00000000000ce;;;f;;;;f;ff;ff;flfffffffgfgfgfgegggggggghghgghghhhhhhhghhghhghhhghhhghhchhhhhhhhhhhghghhhhhhhhhhhghghdddja00000000000000 ..+#$..-'''']#$@+++$++++++@#*,,''''''',!,~,,~,~,~,~,~~'~~,~~****~**~**&>//////^////m*~*~**!~!!!!~~,''''',.@@@@$@@.1$81818897i7777777777||||||[||[[[[[[<}<<<<:::::4:44545455555////!~~~~~~~!~!~!,''',,$@@@@@@> ", +" 0000000000bl;;;;;;f;ff;f;f;ffffffffeffgffgfgfgggfgggghgghghghghhghghhhhghhhcghhhhghhhghhhghhgcghhhhhhhghghghghghhhhhhhhddda0000000000000 .#+.#+.-,'!%+++++$@+@+$@++&,,'''''',,!!!~,~,~,~,~,~,~,~~,~*{********m^//////^56^///@*~**~~~~!~!~!!!''''''*@@@@@@@@$$18181387i7i77777|7|7||||||[[[[[[[<[<<<<<:<::::44:444555565////~~,~,~~~!~!~!''''',@@@@@$@> ", +" 0000000000cl;;;;;f;;;;;;f;fflf;fffffgffegfgfgggggggggggghghghghhghhhhhghhhhghhhhghhhhhhhghhhhhhhhghghhghhhhhhhhhhhhhhghghhhddda0000000000000 #+#....-'-.++++@++++++++&,'''''''''!,~,,~,~,~,~,~,~,~,~~****~**~***#//////^n6665////&**~**~~!~!!!~~,''''',&@@@@@@@+$$1818883i77i777777|77||||||||[[[[[<}<<<<<::::::44444445556///(~~~~~!~!!~!~~,''',,@@@@@@@> ", +" 0000000000cl;;;;;;;;;fff;f;f;fffffffffffgffgfgfgfggggggghggghghhghhhhghhhhhhhghhgdhhghhghhhhghhghhhhhhhhchhhghhhghgchghhhhhhhhhddda0000000000000 ..+#+#+%.++++++++++@+@&!,''''''',!!!!!,~,~,~,~,~,~,~,~***********$^//////n666666_///^**~~*~~~!~!~!~~''''',!@@@@@@@@$$$8181887i777777777||||||}|[[[[[[[<[<<<<:<:::4:44444545555////,~!~!~~!~!~~!''''',@@@@@@@> ", +" 00000000bc;;;;;;;;;;f;;;f;fff;ffffffefffffgfgfggggggggggghghghghhhgchhhhghhghhhhhhghdghhdghhhhhhhhghhhghhghhhcghhhhhhhhhhhhghhghhdddda000000000000 #...$++++++@+$+$@+++#*,''''''',,,~,~,~,~,~,~,~,~,~,~***{~***~**&^//////_666666666////$~**~*~~!~!~!!!!''''',&@$@@@@@+$1$8188887i7i777|77777||||||||[[[[[<}<<<<<::::::4444455555///(~~~~,~!~!~!~~''''',>$@@@@@> ", +" 000000000bf;;;;;;;;f;;;f;f;f;f;ff;fffffffgggfgfggggggggghghgghghghhhhhghhhhhhhhhghhhhhhhhghhhghghhghhhghhhhhghghhhhghghghghhhhhhhhghhdddda000000000000 ..$..++$++$++@++++$*,,,'''''',!!~,,,~,~,,,~,~,~,~~~*~*****~**&^//////_56666666666n////**~*~*~~!!~!~~!,''''',.@@@@$@@+1$818188i77i777777|7|||||[|[[[[[[<[<<<<<:<:::4:4444445555////~,~!~~!~~~~!~''''''@@@@@$@> ", +" 0000000000e;;;;;;;;;;;f;f;f;ff;fffffefffgffffgfggfgfgggggghgghghgdghhghhghchghhghhhghghhghhhghhhhhhhhghhhhhghhhhhghhhhhhhhhhhghghghhhhhghdddoa00000000000 $)+++++++++++++++-,,''''''',!!,,,~,~,,~,~~'~,~,~~**{****~*~&$//////^n6666666666666^///$~*~*~~~~!!~~!~!''''''*@@@@@@@@$1$818883977i7777777|7||||||[|[[[[[<}<<<:<::::4:444454555///(!~~!~!~,~~!~~,'''',@@@@@@@> ", +" 000000000c;;;;;;;;;;;f;;;;f;ff;fflffffffffegfggfgeggggggghgghghghhhhhhhhhhhhgdhgdhgdhhhhgdhgchhghcghhhhhghghhhhhhhhhghhhghhhhhhhhhhchghhhhhhdddj000000000000 .++++++++++++++&!,''''''',!,!!~,~,~'~~,~'~,~,~~~~****~****$^//////n666666666666666n////&*~*~~~~!~~,~~!,''''',#$@@@@@@@$18118887i777777|777|||||[|[[[[[[[<<<<<<:<::::4444455555////~~,~,~~~~!~!~''''',@@$>@@@> ", +" 00000000bl;;;;;;;;;;;;f;f;f;f;f;fffffffeffgfgfgfggggggggggghghghghghghghhghhhghdghhhghghhghhhhhhhhhhhghhchhhhghhghhhhghhhhhhgcghhghhghhhhhghhgdddda00000000000 +++$+++$++$+++#~,'''''''',!,~,,,~,,,~'~,~,~,~~*{*********&///////n666666666666666666_///^~*~~*~~~,~~~!~~''''''!@@@@@@@@$$$8181883i7i77777|7|7|||||||[[[[[[[<<<<<::::::444444555///(,~~~~~!~~!~~~'''',,@@@$@@+> ", +" 000000000e;;;;;;;;;;;;f;;f;f;ff;fef;fffffffgfgfegggfgggggghghggghhghhhhhhhhhhghhhghhhgdchhhhhghghhghhghhhghhhhhhhghhghhhhhghghhhhhhhhhhhghhhhhhhhhdddda00000000000 +++++++$++@+@+#*,,'''''',,!!,!'~,~,~~,~,~'~,~~~~****~*~**8^//////_666666666666666666666////#~*~*~~~!~,~,~~,''''',-@@@$@@@@$$8181883i777777777||||||[[[|[[[<[<[<<<:<:::4:444454555////~!~,!!~~~~!!~''''',@@@@@@@> ", +" 00000000e;;;;!;;;;;;;;;;;;f;f;ff;fffffffffffgfgfggfggggggggggghhghghhghhghhghhhhhgdghhhhghghhhhhhhhghhhhhhhghghghhhhhhchghhhhhghhghhghghhhghhghhghghdppdj00000000000 +++++++++++++++-,,''''''',!,~,~,~'~'~'~'~,~~,~~*********~&@//////_n6666666666666666666666n////*~~~*~~~~!~!~!~,''''',$@@@@@@@$$$1818887i7i7777777|7||||[|[[[[[<[<<<<<::::4::44445455///(~,~~~,~!~~~~~,''',,@@@@@@@> ", +" 00000000e;;;;;;;;;;;;;f;f;f;f;f;fffffeffffgfgfgfgfgggggggghghghghhgdgdhhhhhgdhghghhchhhghhhhhghhghhhhhghhhhhhhhhhhghghghhhhghhhhhghhhhhhhhhhhghhhhhhhhhddpda0000000000 ++++$+++++++++&,,''''''',!,!,,,~'~,~,{,~'~'~,~~*{**~*~*~q$///////n6666666666666666666666666^///$*~~*~~~~~~!!~!!''''''=@@@@@@@@$$81818887777777|77|||||||[|[[[[[<<}<:<<::::4444445455////~~,~!~~~~,~!~''''',@@@@@$@> ", +" 00000000b;;;;;;;;;;;;;;;;;f;;f;fffff;fffeffgfefggfgggggggghgghghghghhhghghghdghhhhhhghhhghhhghhhhhhhghhhhhghhghhhgchhhhhhhghhhhhhhhhhghhhhghghhhhhhhhhhghhddpdj00000000000 ++++++++$++++@&!,'''''''',!,~,~,~'~,!,,,,,~,~~{**********#///////n566666666666666666666666666n////&*~~~~~,~~!~!!~,''''',#@@$@@@@+$$81818397i777777777|||||[|[[[[}[<<<<<:<::::::4444555///(~!~!~,!~~~!~~'''',,@$@@@@@> ", +" 00000000e;;;*;;;;;;;;;;f;f;;ff;flf;fffffffffgfgfgfggfggggggggghghghghhhchhhhhghhhghhhhghhhhhhhhghhghhhhhgchghhhhghhhhhhhghhhhghghghhhhhhghhhhhhgchghhhghhhhhhdddda0000000000 ++++$+++++++$+#*,,'''''',,!!,!'~'~,~'~'~!~~'~~~****~*~**~&^//////_556556666666666666666666666666_///>*~~~~~~~,~!~!~!''''''!@@@@@@$@$$1818188777i77|7||7||||[|[[[[[<[<[<<<:<:::4444444455////,~,~!~~~,~~!~''''',@>@@@@@> ", +" 0000000ae;;;;;;;;;;;;;;;;f;f;f;ff;effffffffgffgfggfgggggggghghghghghhhghghhghhcghhhhghhhhhghghcghhhhhhghhhhhhhghhhhhghghhhhghchhhhhhghghhcghghhhhghhghhhhhhghhhdpppa0000000000 +++++++++++++++=,'''''''',!,~'!~'~,~'~'~,~,'~~~***~*****~&^//////_n55556555566666666666666666666665////#~*~*~~~~~!!~!~!''''',&@@@@@@@@$$1818883i7777777|7||||[|||[[[[[<[<<<<:::::::4454545////!~!~!,~~~!~~~,'''',@$@@@@@> ", +" 0000000b;;;*;;;;;;;;;;;;f;;f;f;f;fff;fffefffgfgfgfggegggggggghgghhhghhgdhhhhhhghhhhgdhhghhhhhhhhhhhgchghhghghhhhhghhgdhhhghhhhghhghhchhhhghhhhhhghhhhhghhghhhhhhhddpdj0000000000 ++++++$+++$++++&,,''''''',,!,,!~'~,~'{'~!!,~,~~~**~****~*m$//////^n5555555565565666666666666666666666n////*~~~~~~!!~!~!!~,''''',+@$@@@@@$$$81188377777777|7|||||[[[[[[[<[<<<<<:<::4:44444545///(~,~,~~~!~~!~!''''',@@@@@$@> ", +" 00000000e;;;;;;;;;;;;;;f;;;f;;f;fff;ffffffffffgfgegfggggggggghggghghghhhhhghhghhhhhhhhghdghhghhghhghdghhhhhhhhhghhhhhdghghhhghhhhhhdghhghhhhghhghhhhghhhhhhhhghhhhghhpdpda000000000 +++$.+++++++++&,'''''''',,!,~'~,,~'!,!,~'~,,~~***{***~***#///////n5555555555555555566666666666666666666^///$*~~~~~~!~!!~!~!''''''-@@@@@@@@$$$$$$1$1187777|7|7|||||||[[[[<[[<<<<::::::4:445455////!~!~,!~~!~~~~''''',@@@@@@@> ", +" 0000000be;;!;;;;;;;;;;;;;;f;f;f;ff;ffffffffgfgfegfgfgggggggghgghhghghhhghhhhhhhhhghhghhhhhhhgdhhhhhhghhhghhhghhhhhhchhhddddddddddddddddddddddddddhhhhhhgcghhgchghhhhhhhpdppa000000000 +++.++++.+++++#~,,'''''',,!,!,!,!!'~~'!!,!,~'{~~********r&^//////_444444554555555555655666666666666666666n////&~~~~~~~!~!!~!~,''''''#@@@$@@@>//////////777777|7||||[|[|[[[[<<[<<<<::::4:4444455///(~,~!~~~!~!~~~''''',@@@@@@@> ", +" 0000000a;;;;;;;;;;;;;;;;;;f;;f;ff;effffffeffffgfggfgggfgggggghgghghghghhhhghghghgdghhdghghghhhghghghhhghhhhhdddcddddddddddddddddddddddddddddddddddddddddddhhhhhhhghhghhghdppdo0000000000 ++.++++++$++$+#*,'''''''',!',~,~'~'~~'~,~,~,!!**{*~***~*~&(//////_444454544544545555555555556666666666666666_///>~*~~~~~!!~!~!~!''''',~@@@@@@@@(/////////77777|7|7|||[|[[[[[[[<<<<<:<::::4:444545///(~!~,,,~~~!~!~,''',,$>$@@@@> ", +" 0000000b;;;;;;;!;;;;;;;;;f;f;f;f;fff;ffffffgfgfgfgfggfggggggghghghghhchhghhhhhhhhhhhghgdhhhhhhgdhhhhhhhdhdddhdddhddddddhdhddddddddddddddddddddddddpdddpddddpddddhdhhhhhhhhhdpdpda000000000 +++++$++++++++#&,,''''''',,,!!,'~'~,~'~'~'~,,~~*******~**m$//////^n44444444444544454555555555655666666666666666////&*~~~~~~!~!~!~~,''''',%@@@@@@@@////////(7777777|||||||[|[[[<[[<<<<<:::::44444454////,~!~~~~!~~~~!''''''@@@@@$@> ", +" 0000000c;!;;!;;;;;;;;;;f;;f;;;f;ff;fffffffffegffgfgfgggggggghggghghghhghhdghhcghhhghhhhhghhhcghhhghhdhddddhdhdhdhdddhdhddddddddddddddddddddddddppdddpdddddpddpdppdddddhdhhhghdpppoa000000000 ++.+++.++$.+++&,'''''''''!,!!'~'~,~'~,~,~'~,~~**~****~***+///////n4:4:444444444445445445455555555556666666666666n////**~~~~~~,~~~!~~,''''',$@@@@$@@>////////377777|77|||[|[|[[[[<[<<<<<::::4::444455////,~!,,,~~~!~~~'''',,@@@@@@@> ", +" 0000000b;;;;;;;;;;;;;;;;;;f;f;fff;fff;fefffffgfgfggfggggggghgghghghhghhhghhhhghhhhhhchghhcghhhhhhdddhdhdhddddddddddddddddhddddddddddddddddddddddddpddodddppppdpdpdpdoddpdddddhhdppppa000000000 ++.+$+.++.++++&!,''''''',,,!,,!,!~'~,~','~,~'~{~*********1///////_:::4:4::4:444444444444545455555556565666666666666////#*~~~~~~~!~!~!!!''''''-@@@@@@@@////////3777|77|||||||[[}[[[[<[<<<:<::::44444455///(~,~~~,~~~!~~~''''',@@@@@@@> ", +" 0000000g!;;;;;;;;;;;;;;;;;f;;f;f;fff;ffffffgfffgfgfgggggggggghggghghhhghhhhghhhghhhghhhhhhhhhhhdhdhcdddddhdhdhdhhdhdhdhddddddhdddddhdddddddddddodddddddpdpddddppdpdpdpdodppdpdddddppppa000000000 ++.++++$++++++#*,,''''''',,!!'!!'!''~'~'!~,~,~******~*~**&>//////_::4:::4::4:4:4:4:4444444444454455555555556666666666n////*~~~~~~!~~!~!~~,'''',,#@@@@@@@@stu///(337777|77||||||[|[<[[[<<<<<<::::::444445///(~,,,~,~~~~!~~,''',,@@@@@@@> ", +" 0000000e;;;!;;;;;;;;;;;;;f;f;f;f;f;effffffeffggfgfgggfgggggghgghghhghghhhhghhhhhhhghhhghghhhhdhdddddhdhhddddhcddddddddddhddddddddhddddddddddddddddoddpddpddpppdpdpdppdpdpdpppdopdpppppppa000000000 ++.++$+.+++$+.#*,,''''''',,,'!~,~'~!~,~'~'~'~!~~*~*******m^//////^k::::::::::4:::4:44:4444444454454545555555655666666666^///>~~~~~~~~~~~!~!~''''''*@@@@$@@@sts////8377777|||||[|[[|[[[<[<<<<:::::444444545////!~~,~,~~!~~~!''''',@@$>$@+> ", +" 000000be;;;;;;;!;;;;;;;;;;f;f;f;fffff;ffffffgeffgegfgggggggggggghghghhhhhhhdghhghhhhghhhdhhddddhdhhdhdhdddhdddddddddhddddddddddddddddddddddddddddddddpddddppdpddpdodpdpdpppdodpppppppdppppj000000000 ++.+++.+.++.+.+&,,''''''',,!!!!','~!','~'~,~'~~*{*****~***+//////^n:::::::::::::::::::4:::4:4444444445445455556555656666665////&~~~~~~~,~~~!~!,''''',&@@@@@@@+tu////383777|77||||||[[[[[[<[<<<<<::::::444445////,,~,~!~~~!~~~'''',,@@@@@@@> ", +" 0000000e;;!;;;;;;;;;;;;;f;f;f;f;f;f;ffffffffffgfgggfggggggghghghghhghhghghghgdhgdghhhhhhhddhhdhddddhddddhddhddhdhdhdddhdhdhdddddcdddddddddddddddddddddddppddpdpdpddpdppdodppdpppppppdppoppdpj000000000 +.++$.++++++++&,,,'''''',,,,'!,,~~',~'~,~'~'~~~******~*r*$^//////_<<:<<<:::::::::::::::::44:4:4:4444444544554555555556566666_///(*~~~~~~~~,~!~~!''''',!@@@@@@@@@(////1337|77||||||[|[|[[[[<[<<<<:<:::44444454////,~,~,,~~~~!~~''''',@@@@@@$> ", +" 000000b;;;;;;!;;;;;;;;;f;;;;;;f;fffffffeffefgffgffgfgggggggggghghggcghhhhhchhhghhhhghdhdddhhdhdhdhhdhdhhdhddhdddhddddddddddddhddddddddddddjdddoddoddddddpddpppddpdppdodpddpdpdppdpvpppppppppddo000000000 .+.++.+++.+.$.#!,'''''''',',!!!'!''~,!'~'~'~,~~******~***&(//////_<<:<<:<:<<<<<::<::::::::::::::4:4:444444444545455555565656665////#~~!~~~~~~!~~!~!''''',&@@@@@@@@(////818377777||||[|[[[[[[<[<<<<<::::::444445///(~,~,~!~!~~~~~,''',,@@@@@@@> ", +" 0000000e;;;;;;;;;;;;;;;;;f;fff;ff;f;fffffffgffgfggfgggggggggghggghgdghhhghhghhhhghchdhdhhhddddhdhddcddddddhdddhdhddhdhdddhdddddddddddddddddddddddpdddjdddoddddppdpdpdpdpppdodoppppppdoppppppppdpp00000000b ++.+$.++.$+.++#*,,'''''',,!,!'!,!,~,~'~'~'~,,~{**{**~****&^//////_k<<<<<<<<<<<:<<<<:<:<:<::::::::4:::4:4:444444454555555555656666n////*~~~~~,~~!~~!~!,''''',#@@$@@@@>////138337|7||||||[|[[[[[[<<<<:<::::4:444545////,~'~'~~!~~!~~''''',@@@@@@@> ", +" 0000000e;!;!;;;;;;;;;;;;;;f;;f;f;fef;fffffffffgfgfgggegggggghgghghghhghhhhhhhhghhhdhdhdhddhhhdhdhdhdhdhdhdddddhddddddddhdddddddddddhddddddddddpdpdddppddpdddppdpdpdodpdppppddpdpppppdppppppppppppdj00000000b .+.++.+$.+++++#-,''''''''',,'!,,,'~',,'~'~'~,!~**~******~&$//////^k<<<<<<<<<<<<<<<:<<<<<<:<:<:::::::::::4:4:444444444445555555565666^///@~~~~~~~~~!~~~~!'''',,*@@@@@@@@(///111337|77|||||[|[[[[<[[<<<<<::::4:444445////,,~,!,~~!~~!~''''',>$@@@@@> ", +" 0000000e;;;;;;;;;;;;;;;;f;f;;f;f;fffffffffegfgfgfgfggggggggghgghghgdghhhhghghhhhdhhdhhhdcdhddhdddhdhdddhddhdhddcdddhddddcddhddddhdddddddddddddddddpdpddddpdpdppdppddppdpdpdopdppppppppppppppppoppoppo00000000b #.++.+++.++.+.$&,,''''''',,,,!,!,!~'~'~~'~'!'~~{******~*~q$///////2<<[<[<[<<<<<<<<<<<<<<<:<<<:<<:<:::::::4::4:4:4444445455555555556556n////&~!~!~~!~!!!!~~,'''',,%@@@@@@@@///1313337||7||[||[|[[[[<<<<<<:<:::4:444545////,~,,~,~~~~~!~,'''''@@@@@$@> ", +" 000000e;;;;;;!;;;;;;;;;;;;;f;f;ff;f;ffffffffegfgfgggfgggggggghghghghghhhhhhhghchhdhhdhdhhdhdhdhdhdhddhdhddcddhdddddddhdddddddddddddddddddddddpddoddddddpddppddpdpdppdppdodddpppppppppppppppoppdpppppdj00000000b%####.$+.+.+$++%!,'''''''',,'!,!''~','~','~'~!~~**~***~***&(//////_<[<[<[<[<<[[<[<[<[<[<<<<<<<<<<<:<:<::::::::4::4:4444444444455555556666_///>~~~~~~!~!~~~~~!'''',,~+@@$@@@@>//11113377|||||[|[[[[[[[[< ", +" 000000c;;!;;;;;;;;;;;;;f;f;f;fff;ffffffffffffgfggfggggggggghghgghghhhhhghhhhhddhdhhdhdhddhdhdhddhdcdhddddddhddhdhdhdddddhddddddhdddddddddddddddddddpdpdpppdpdpdpdopdppdpdppppppppppdppppppppppppopvppppa00000000)####..+.++).#*,,'''''',,,,!,!'~,~'~'~'~'~,,~~******~***&^//////_}[[[<[<[<[[[<[<[<[<[<<<<[<<<<<<<<<<<:<<:<::::::4::4:444444454555555556565////#~~~,~~~!~!!!~~!'''',,&@@@@@@@$//1811333|7|||||[|}[[[<<[< ", +" 000000c*;;;;;;;;;;;;;;;f;;f;f;f;ff;ffffffefgffgfgfggfgggggggggghghghghhhghghhdhhhddhdhhdhdhddchddhddhddhdhdhddddddddddhdddddddddddddddddjddddddpddddpdddddpddodpdpdpdpdppdpdpppppppppppppppppppvpppvpppppa00000000+#####$+.$#-,'''''''',',,,,',,','!'~'!,~'~~**~***~**~&>//////^k[[[<[[[[[[[<[[[[<[[[<[[[<[<[<<<<<<<<<<<<:<:<:::::::::4:4444445445555555655n////*~~!~~!~!~~~~~~,'''',,+@@@@@@@@/111113337||||[|[|[[[[<[<$@@@@> ", +" 000000b;;;;;;!;;;;;;;;;;;;f;f;f;ffffffffefgffgfgfggggggggghghghhghhhhhhhhhcdhhdhdhhhdhdhdhdhdhdhdhdhdhddhddddhdhddhdhddddhddddcddddhdddddddddddodddpdddodpdppdpdpddpppdodpdopppppppdoppoppopppppppvpppppvppj00000000######+.&,''''''''',!,!'!!!!'~!,~'~'!'~{~*{***~***q$//////^2[[[[[[[[[[[[[[[[[[[<[[[<[[<[<[<}<<}<<<<<<<<:<<:<:::::4::4:4444444445555555656^///+~~~~,~!~!~!~!~!''''',*@@@$>@@@^^$$$^1_11122122[[[[[[<[< ", +" 000000c;;;!;;;;;;;;;;;f;f;;f;f;f;f;fffffffffgfgfggefggggggggghgghghghhghgdhdhdhhdhdhdhdhdhhdhdhddhdhdddddhddhddhddddddddhddddddddddddddddddddddddddddodddpdpddddodpppdppdpppdpddppppppdppdppppppoppppvopvvvpoa0000000b#####%!'''''''',,!',,,!''~'~',',,,!,~~*********&.///////_}[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[<[<[<<}<<<<<<<<:<<:::::::4::4:4444544455555556n////&,~~~~!~!~!~~!~,'''',,#@@@@$@@@//////////////$[|[[[[<[< ", +" 000000bl;;;;;;;;;;;;;;;;;f;f;f;ffffffffffffgfgfgfgggggggggghghghghhghhhhhhdgdhdhdhdhhdhhdhddhdhdhdhdhdhdhdhddhddddhdhdcddddddddddddddddddddddjddddpdddddpdpdpdoddppddpddppdpdppodpppppvppvpppppppppopppppvppvpoa0000000b$#%=*],'''''',,,,,!',,,,,,'~,~'~'~~****~***m&$^//////^k}}}[[[[[[[[[[[[[[[[[[[[[[[[[[[[<[[[[<[[<<<}<<<<<<<<:<:<::::::4:4:4444454455555556_///>~~~,~!~!~~!~~~~''''',~@@@@@@@@>/////////////^[[[[[<[<< ", +" 000000l;;;;;;;;;;;;;;;f;;f;f;f;ff;fffffefgfffgfgfgfggggggggggghghghhghhhdhdhdhdhhdhdhcdhdhdhdddhddhddddhddddhdddcddddhdddhdddhdddddddddpddddddpdddodddpddpdpdpdppddppoppdodpppppppppppppppppppopvpppvvvpvvpvvvpp00000000)-*]]]]'''',!'',,'~'!,!'~,,,'~'~~****&#$+$++>//////^2222k}}}[|[|[|[|[|[|[|[|[[[[[[[[[[[[[[[<[[[[<[<<}<<<<<<<<:<:::::::4::4444454445555555////%~~~!~!~!~!~!~~!'''',,&@@@@@@@@////////////(_|[[[[[<[< ", +" 000000c;;!;;;;;;;;;;;;;;;f;f;f;ffffffffffffgfgfggggggggggghghghgdghhhhhchdgdhhhdhdhdhdhdhdhdhdhddhddhdddddhdddhdhdddddddddddddddddddddjdddddddddpdddddpdpdpddpdddpppddddpdpppppppppppppppdodppppppvpvpvppvppvvvovo00000000=!]!]]],',,,!,!,'!'~'!''!'~'~~**{*&@@@@@@@$@>(///_2k222223}}}|[[|[|[|[|[|[|[|[|[}[[}[[[[[[[[[<[[<[<<<}<<<<<:<:<:::::::4:4:44444545555555n////~!~~!~~~~~!~!~~,'''',,+@@@@@@@>////////////$[[[[[<[<<$@@> ", +" 000000c;;;;;;!;;;;;;;;;f;f;f;ff;f;ffffffffgefgfgfgfgggggghggghghghghghdhhhhhdchdhhhdhhdhdhcddhdhddgddhddhhddhdhdddhdddhddddddddhdddddddddddpdpddpdpdddddpddpppdpppdodpppppdpppdpppppppppppppppppopppvppppvvpvvppvppj0000000c=]]]]*]~',,,','~,,,'~'~'~,~~****+@@@@@@@@@+@+>^22222222222}3}||||[|[|[|[|[||[|[|[|[|[[[[[[[[[[[[[[[<<<}<:}<<:<<<::::::::4:44444544555555////.~,~~!!,~!~~!~!!'''',,*@$@@@@@@(///////////_|[[[[<[<< ", +" 000000bl;;;;;;;;;;;;;;f;;f;f;ff;feffffefffgfgfggfgggggggggghghghgdghhdhdhddhhhdhdhdhhdddhhddhdhdcdddcdhdddddddddddddhdddhddddddddddddddddddddddpddddppdoddpdddpdpdpdpdddodppdopppppppppppopppppppppoppopvvpvppvvvvvvvj0000000)]]]]=]=~'!'~,,''~'~'~',~{*****.$@@@$@@@@@@@@$@$_2_222222222}3}||||||||||[||[|[|[|[|[|[|[}[[[[[[[<[[[<<[<<<<<<:<:<::::4::4:4444445455555n////&,~,~~~~!~!~~~~,'''',,#@@@$@@@>///////////^|[[[[[<[< ", +" 000000e!;;!;;;;;;;;;;;f;f;f;f;ff;ffffffgefffgfgeggggggggghgghgghghhhhdhhdgdhddgdhdhdhhdhdhddgdddhhdddhddhdhdcdhdddddddddddddhddddddddddddddpdpdddpdddddpddppdodpdpdpdoppddpppdppppppppppdpppppoppvpvvpppvpvvovvpppvvooa0000000l==]]*]]~,,'~'~,,,,,,!*~**{*~#@@@@@@@@@@@@@@@@@@$222222222222}3}|||||[||||||||||[|[|[|[|[[}[[[[[[[<[[<[<<}<<<<<<<::::::4::44444445455565_///>!~!~,~~~~~!~!~~''''',*@@>@@$@@>/////////(_[[[[[<[<< ", +" 000000c;;;;;;;;;;;;;;f;;;f;f;ffffffffffffgfgfgfggfggggggggggdghgdghcdhhdgdhdhhddhhdhddhddhdhdddhdddhdhdddhdddddddhddhddhddddddddddddddpddddddddjddddjdddpdpdpdddodpdppddpppdppppppppppodoppppopppvpppvpvpvppvppvvvvvvvvo0000000b-==]*=*]~'~',''~'~'~*******&$@@@@@@@@@@@@@@@@@@@>$221222221223}3}|||||||||||||||||[|[|[|[|[[[}[[[[[[[<[<<<<}:<:<<:<:::::4::4444445454554////&~!~!~,!~!~!~!~,''''',&@@@@@@@@//////////^|[[[<[<<< ", +" 000000c;;;;;;;;;;;;;;;f;f;f;f;ff;ffefffffffgfgfgggggggggghghggghhgdhhdhdhdhdhdhdhdhdhhhdhhhddhddgdhddhdddhddhdhddddddddddddhdddddddddddddddddpddddpdddpddpppdpdppdpppddodppopppppppppppdppppppppvvvvvppvvvpvpvvvvvvvvpvvvj0000000b=*=]]]*]!,,,~,',~*{**~**~*#@@@@@@@@$>$@@@@@@@@@>@^$111212321223333|||||||||||||||||||[||[|[|[[[[[[[[[[[[[<<<<<<<:<::::::4::444444455555_////~!~,~~!~!~~~~~~,'''',,+@@@@@@@>////////(_[[[[[<[<<@@@> ", +" 000000bl;;;;;;;;;;;;;f;;f;f;fff;fffffffegffgfgfgfgggggggggghghhgdhdhhhhhhhhhhdhhhdhhdcddhdddhdhddddhddhdddhddhddhdhdhdhdddhddddhdddddddddddjddddddddpddpdpddpdodppdpdpppdpddpdpppppppppopppppoppppppppppppvpopvvvvpvvvvpvvvj0000000)===]=*]*!'!'~~*********~&@@@$@@$>)*&+@@@@@@@@@$@@@$121121213223}33||7|||||||||||||[|||[|[|[|[|[[[[[[<[<[<<}<<<<<<<:<::::4:4:44444455555////.~!~,!~~~!~!~~~!''''',-@@@@@@@@/////////$[[[[[<[<< ", +" 000000l;;!;;;;;;;;;;;;;f;f;f;ff;ffffffffffgfgfgggggggggghgghghghghdhdhdhdhddhhdhdhdhhhdhdhddhddhdhddhdddhcddhdddddddddddddddddddddddddddddddddpdpdpddpddpdppdddpdppdodpdppppppppppppppdpppppppvvoppppvvovvpvpvppopvvvpvvvvooa0000000-==*]]=]~~'~~~*****~***&@@+>@@@@#,,,,&$>$@@@@@@@@>>>$1111211211233337|7|||||||||||||||||||[|[|}[[[[[[[[[[[<}<<<<<:<:<:::::4:44444445455n////*,~~,!,~!~!~!!~,'''',,#>$@@@@@@(uu(////^[[[[<[<<< ", +" 000000c;;;;;;;;;;;;;f;f;f;f;fff;fffffffffgfgfeggfggggggghgghghgdddhhhhhdhhdgdhdhdhdhddhdhhdhddhcddhddhdhdddddddhdhddhdhddddddddddddddjhpddddpdpdddddpdpdpdpddpppdpdppdpdodpppppppppppopppppppppppvpoppppppvpvvpopvvvvvvvvpvvvo0000000b===*]=]=*~**{***~***&^^@@@$@@@+~'',,,,%>@@@@@@@@@@@>>11112112121233337|77|7||7|||||||||[||[|[|[|[}[[[[[<[<<[<<<<<<<:<:::::4:44444454555^///>~,~!~~~~!~~~~~!''''''~@@@@@$@@utxs////^[[[[[<<<< ", +" 000000;;;;;;;;;;;;;f;;f;ff;f;ffffefffgfgfgfggfgggggggggghgghghhhhhdhdhhhhdcdhdhdhhdhdhdhddhdhdhdhddhddddhdhddhdddddddddddddddddddddddddphoddddddjddppdddodppppdppppdppdppppppppppdodpdpppppppvppppvpopvvvpvvvpvvvvvvvvvvvvvvvoo0000000%-===]=*-****~*****$//+@@@@@@@%,''',,,,*#$@@@@@@@@$@>@^$111131132123337|777|7|77||||||||||||||[||[[[[[[[[[<[<}<<<<<:<::::::4:44444454554////&~,~,,~!~~~~~!~,'''',,&@@@@@@@@ttu///(^}[[[<}}k ", +" 000000e;;;;;;;;;;;;f;;f;;f;fffffffffffefffgfgfggggggggghggdggdhhdhhhdgddhdgddhhdhdchdhdhdhddhdddhddhddhddddhdddddcdhddddhdddddddddddddddddddjddpdddpddppddpddpppdpdppdodpppppppppppppppppodpoppvvppppvpppvvvpovvppvvpvvvvvvvvvvva000000a-==**=*-*m***~**$///>+@@@@@$+,'''''''',,=+@@$@@@@@@@@@@$$111121131233337|777|7|77|||||||||[|[|[[|[|[[[[[[[[<<<}<<<<:<:::::::4:444454455_///(~~!~~,~~!~!~~~~''''',,@@@@@@@@usu////^}}<[<^//////////////////////(~~,!'!~~~~~~~''''',@@@@@@@> ", +" 000000b;;;;;;;;;;;;;;;f;ff;f;f;fffffffgfgfgfgggfgggggggghggdghdhhdhdhddchhddhhdhdhdhdhdhddhddhdhddhdddhddhddddddhdhddddddddhddddddddddddddddddpdddddpdpdpdodpdpdpdodpdpdpppppppppppppopppdpopppvpppppppvvppopppvvvvpovvvvvvvvvvvyo0000000)=----m*-*****&////($@@$@@@@-'''''''''',,,&+>$@@@@@@$@>$>$111131111113333|7|77|7777|7|||||||||||[|[[[[[[[[[[[<<<<<<<<<::::::444444445555////#!,,~,~!~~~~~~!~'''',,&@@@@@$+@//////^}}[[<^/////////wxw(//////////','!,,~~~~~~~,'''',>$>$@@@> ", +" 000000l;;!;;;;;;;;ff;f;f;f;fffffffffffffgfgggfggggggghghgghgdhhhdgdgdgdhddgdhdhhhhdhdhdhdhdhddgddhddhddddhdhdhddddddhdddddddcddddddddddddpddddddpdpdddpdpdpdpdpdppdpdpppdpppppppppppdppppppvppppopvvvvppvvpvvvvvvpvpvpvvvvovvvvvvvj0000000&--*=*-*&**&^/////@@@@@@@+#,''''''''''''',,%@@@@@@@@@@@@>>$1111313112133877777|7|77|7||||||||[||[||[|[[[[[[[[<[<<<<<<:<::::::4444444455n////*~~,~,~!~~!!~~~,''''',#@@@@@@@>/////^k}}[<^/////////www(//////////,,,~''~!~~~~~''''''@@@@@@$> ", +" 000000c;;;;;;;;;;f;;;;f;f;ff;ffefffffegfgfgfgegggggggggghghgdhhdhddhddhdghdhdhhdhdhdhdhdcdhdhdddhddhdddhdhdddddddddddddddddddddddhjddddddddpddpdddddppddpdpdppdppdpdpppdpppppppppppppvdoppppppopppvvpppopvvvppvpvvypvvvvvvvvovvvvvvoa000000b-&&-&*&*&$(/////^@@@@@@@@*'''''!,'''''''',,~#@@@@@@@@@@$@>>$11111318111333377777|77777|7|||||||[|[|[[[[[[[[[[<[<<<<<:<:::::::4:44445445^///@,~,~,~!~!~~~!!!'''',,*@@@@@@+@(////^2}k[<^////////(www(/////////(,,'!,!~~~~~~~,'''',+@@@@@@> ", +" 00000b;;;;;;;;;;;;;ff;ff;ff;ffffffeffgffggfeggggggggggdgghdhhhdgdhgdhhhdddhdhddhhdhdhdhdhdddhddhddhdhddddddddchdhdddddhdddddddddpddddddjddddpddpdoddpdpppdpdodpdpdodpdopppppppppppoppppdppppopppvppvpopvpvppvypvvpvvpypvyvvvvvvvvyvyo0000000)*&*-*&#>@>////2++@@$@@$%,''',,!~!,'''''''',,*$@@@@@@@@@@@$>@$11111131318333777777|7|77||7|||||||||[|[[[[[<[[<<[<<<<<:<::::::4444444545n////&!,~,,~~~~~~~~~,'''',,#@@@@@@@@////^kkk}<^/////////tst///////////,~'~',~~~~~~~''''',@@@@@@@> ", +" 000000e;;;;;;;;;;;f;;f;;f;fffffffffgfffgffgggggggggghgggggdgdhhdcddhhdhhdgdhdgdhdhdhdhdddgdhddhddhdddddhdchdhdddddddhddddddddddddddddddddpdjdddddddpddppdpdpddpdodpdppdpppppppdppppdpppppopvppppvpvppvpvpopyppvpvvpvvvvvvvvvvvvvvyvvvva0000a0a&--&&@@>>@>/2[$@@@@@@@+,'''''!~!!!,,'''''''',,-@$@@@@@@@@@@@>$$111111111133337|7777|777|7|7|||||[|||[|[[[[[[[[[<[<<<<:<::::::::44444555_///(!~,~!,!~~!~~~~!,'''',,$>$@@@$@>///^22kk<^///////////////////////'',,,!~~~~~~~,''',,@$@@@@@> ", +" 000000c;;;;;;;;;;f;;f;f;ffff;fffffffffgfgggfgfggggggggghhhdgdhdgdhhhdhhdhddhdhddhcdhdhdhdddddhddhdddhdhddhddddhdhddhdddddddhddddddddddddddddddddjdpdpdppddpdpppdpdpdpdodppppppppodppppodppvpvppvpvpvvpvppvvpppypovvvvvpvvvvvvvvvvvvvvvvy0000000)-&#+@>@>>$3}3@@@$>)@@-,'''',~,~~~!!,'''''''''',%@@$>@@@@@@@@@>$$11181811183333777777|77|7||||||||[|[|[|[[[[[<[<<[<<<<:<::::4:4444444554////#,~,~,!!~~~!!~~!''''',-@@@@@@@@^//^kkkkk^///////////////////////,,,,'!~~~~~~~''''',@>@@@$@> ", +" 000000l;;;;;;;;f;;f;f;f;f;fffffffffgffgfgfggggggggghghggghdhdhdhhdhhchdhdgdhhdhhddhdhddhdhdhcdhddhdhddhdddhddddddddddddddddddddddddddddpdddpdpddpdpdpdpdodpdpdppdpdodpdpppppdppppppopdpvvpppvpvpvdoppvppvvpvopvvvvvvvovvvvvvvvvyvyvyvovvj0000000)+++$@>$833|$+@@@$@@#''''',!~!!!!!~!~,''''''''',~#@$@@@@@@@@@@@@$11118181118833377777777|777|||||||||[[|[[[}[[[[<[<<<<:<::::::4:4445445n////*~,~!!!~!!~~~~~,''',,,+@@@@@@@>//^22kkk^///////////////////////'~'!,,~~~~~~~''''',@$@@@@@> ", +" 000000c;;;;;;;;;;f;;f;ff;fffffffeffffgfgfggfgggggggggghgdddgdhhdgdhhdhdhhdhdhdhhdhdhddgdddgdddddhddddddcdddddhddddhddddddhdddddpddddddjddppdddddddpddppdddppdpdpdodpppppppppppppppppppppppoppvvppvppvpvvvvpyppypvpypvpvvvovvyvyvvvvvvvyvvva000000b...+$133388@@@@@@@@*,''''!~,~!~!~~!~~!,'''''''''']#@@$@@$>$@@@@@@$11111811118333777|7|77|7|7||||||[||[|[[[[[[<[<[<<<<<<::::::4:44444445^///+,,,,~,~~~~~~~~!''''',*@@@@@@@@(/^k2kkk^///////////////////////',,,'!~~~~~~~''''',@@@@@@@> ", +" 000000l;;;;;;;;;;;f;f;f;ff;fffffffegffgefgfggggggghghggdghhdgdhdhdhdgdhdhdhdhdhddgdhdddhddddhdhddhdhdhddhdhdddddhddddddddddddddddddddddddddpdppdpdpdppdppddoppdpddpdppppppppdoppppppppppppdvppopvvpvppvppyppyppvvppyvvvvvvvvvvvvvyvyvvyvyoo000000b)++&893393.)$@+@@@%'''''!,~!~!!!!!!!!~!!,''''''''',-+@@@@@@@@@@@@@@$111813181183337777777|7|77||||||||}||[[[}[[[[<[<<:<<<:::::44:445455n////&~,~,!,~~~!!~~~,'''',,#>$@@@@@@/^2n2kk^//////////////////////^,,',,,~~~~~~~,'''',@@@@@@@> ", +" 000000b;;;;;;;;f;f;f;f;ffffffffffffgfggfggggggggggggghgdhdhdhdhhhdgdddgdhdgddhdhdhddhdhdhdgdddhddhdddddhdddddhdhddddddddddddddddddjdddpdddpdddddpdddpdpddppddppdodppppdpppppppdppopppppopvvvpppppvpvvvvopvppvpvypypvpyvvpvvvvvvvvvvyvvvvyvvoa000000b$8888933&..+@@@$@!,''''~!,~,~~~!!~!!!!!~,,'''','''',&+@@@@@$>$@@@@@$$111181811883337777777|7|7|||||[|[[[|[[[[[<[<<<<<<:<::::::4:4444455_///>~,~,!,!!~~~~~~~''''''!+@>$@@@@>^22nkk^///////////////////////'~'~',~~~~~~~''''',@@@@$@@> ", +" 00000a;;;;;;;;f;;f;fff;f;ffeffeffffffgfgfggfggggggghghghhhhdhhdhhddgddhdhddcdgdhdhdhddhdddhdhddhddddhdddddhdddddddddddcddddddddddddddddppdddpdpddppdppdpppdpdpdpdppdppppppdppppppdpdopppvpvdopvpvpvpvppvppypypvpppvvvpovvovypyvvyvvvvyvvyvyvo0000000888&8988$.##+@@@-''''',~~!~!,,~~!!!~!~!!~!,''''''''''~&@@@@@@@>$@@@@@$$$1181811188333777777|77|7|||||||[|[[[[[[[[[<[<<<:<<:::::444444454////%~,!,!,~~~~~~~~!'''',,&@@@@@@@>^_2k_k^//////////////////////(,',',,~~~~~~~,''',,$@@@@@@> ", +" 000000b;;;;;;;f;;f;f;f;ffffffffffgfggfggggggggggggdgghdddhdgdhdhhdgdhdhdhhhhdhdddhdhdhddhdddhddhddhcdddhdddddhdddddhdddddhddddddddddddddddpdjhpdppddpdjddppdododpdpdodppppodppoppododppppvpvpppvpvvppovvpyppvpypypypypvpvvvvvvyvvvvyvvyvyvyvvva000000)8888m8##..$#+@.,'''',!!,~,~~~,!~~~!!!!~!~~!,''''''''',~#@@@@@@@@@@@@@$$1$118181118333377777|7|||||||||[|[|[[[[[<[<[<<<<:::::::::4444445_////~!~,~,!~~~~~~~~,''''',+@@@@$@@>k_kkk////////////////////////,,~',,~~~~]~~''''',@@@@@$@> ", +" 000000l;;;;;f;;f;f;f;ff;fffffffffgffgfgfgfggggggggghgghghhdhdgdhdhdhhdhhddhhdhdhdhdddgddchddhdddhddhdhddhdhddddddddddddddddddjdddddddpdjddddpddpddppdddpppdpdddppppppppppdppppdpdpppvppvpoppvppvpvvppppvpvvpypppvvpvvvvvvvvpyvvvyvvyvvvyvvyvyyo000000a88m88&#.#)#..@*''''',~~!~!!,~~!,,~~~!!!~!!~~!,'''''','',=$@@@@>$@@@@@@@@$11111818118333777777777|||||||||[|[[[[[[<[<<<<<<<::::4:44444555^///+,!,,!,!!~~~~~!!'''',,*@@@@@@@@(////////////////////////////'',~',~~~~~~~,''',,@@@@@@@> ", +" 000000a;;;;;;;;f;f;ff;ffffffffffgefgfgegfgggggggghghgddddhdhdhdhhhhdhdgddhhdhhhdcdhdhdddhddhdddhddddddddhddddhddhddddddddddddddddddddpddddpdpdppdpdppdpddpdpdpppdpdpdpppppppppppvpppppodvdpvppopvpvpovvpopvvppvypypypvvyvvvvvvvvvvvvvvyvvyvyvyvva000000)3m8&###$#.#.#,''''!~,!~,~~~,,~~~!,~,~!!!~!!!!!,''''''''''-+@$@@>@@@@@@@@$$1118118181833377777|77||||[|[|[|[}[[[[[[<[<<<:<::::4:44444n44n////-,~,!,!~~~~~~~~,'''',,#@@@@@@@@////////////////////////////,,',,,~~~~~~~''''',@@@@@@@> ", +" 000000e;;;;;f;;f;;f;f;ff;ffffeffgffgfgggggggggggggggdgdgdhhhhhdgdchdhddhhdhddhddgdddhdhdddhdhddhdhdhdhddddhdddddddddddhddddddddddddpdddpddddpddpdppdpddoddodpppdodoppppppppopppppppppppvpvpopppvpvpvppypvvvvopvpvpvvvvvpyvvyvyvvvyvvyvyyvvvyvvyoo000000a3888%####+##=''''',~~,~!!,~!~~***~!~!!~!!~!~~~!,''''''''',,%@@$@@$>$@@@@@@$$11118118118333|77|7|77|||||||[|[[[[[[<[<<<<<:<:::::::4:_/_55_///>'~,~,,,~~~~~~~]'''',,*$>$@@@@@(///////////////////////////,'~'',~~]~~~~,'''',>$@@@@@> ", +" 000000b;;;;f;;f;f;ff;ffffffffffgffgfgfgfgggggggdgdgddhdhdhhdhdhddhdhhdhhdhdhhdhddddhhddhdhddddhddddhddddhddddhddddhdddddddddpddddddjhpdpddddpdppddpdpppddpddppdpdpdpdpppdppdppppppvppoppvpppppvpvpvvppvvpvvpvvvpyvvypvovvvvvvvyvvvvyvvyvvvyvyvyvyoa000000)3&######.#&*!'''!~,!~,~~,~!!~****~~~~,~!~!!!~!~!,''''''''''!#@@@@@@@@@@@@@@$$11181181183333777|7|7|||||[|[|[[[[[[[[<<<<<<::::4::_////n54////%,,~,!,~!~~!~~~,''''',%@@@@@@$@///////////////////////////,',!,'~~~~~~~''''''@@@@@$@> ", +" 000000g;;;;;f;;f;f;fff;fffffffffgfgfgggggggggggggggdgdgdgdhchdgdhhdhchdhdhhdhdhhdgdddhdhdhdhdhdddddddhdddddddddddddddddddddddddddpdpdddpdpdddpdpdppdpdpppdpppdpppppppppppppppppppppppppvpvpvpvpopppvvvvvvvvvvpypvpvvvvvvvvvvyvvvyvvvyvyvvyvvvyvvyyo00000003###%###.#]]]'',,~~,~!,~,~~~***~**~~~!~!!~!~!~!~!~,''''''''''*#@@@>$@@@@@@@@$$$1181181118333777|77||||||[|[|[[[[[<[<[<<<:<<:::n///////45_////~'~,,!,~~~~!~~~,'''',,+>$@@@@@>//////////////////////////~,,',,~~~~~~~,'''',@@@@@@@> ", +" 000000a;;;f;f;f;f;ff;ffffeffefgfgfggfgfgggggggghggdgdhdhddgdhdhdgddgdddhhdddhdhdddddhdhddhddddhdhdddhddddhddddddddcdddhddddpddddddddddpddddpdpdpddoddppddpppdpdodpdppppppppoppopppppoppvppppppvvpvovpvpvvvvvvvpvvvypvvvyvvyvvvvvvvvvyvvvyvyvvyvyvyyya000000)########-!]]]'!~,!~!~~~,~,~*******~*~~!~!!~!~!~!!~!,'''''''',,=+@@@@@@@@@@@@@$$1118118111833377|77|||||||[|[}[[[[[<[<<<<:<:k/////////n45////.~'~'!,~~~~~~~~!''''',=@@@@@@@@//////////////////////////''~','~~~~~~~''''',@@$>@@@> ", +" 000000e;;;;f;;f;ff;f;fffffffffffgfgfgggggggggggghgdhhhhhhddgdgddgdddhhddgdgdddhhdhdhddcddhdhddcdhdcddhddddddddddddddddddddddddphjddppddpdddpdpdppddpddodppppppdpppppppppppdppdpppppppvppopypvppvpppovpvpvpvvvvpypvvvvyvvvvvvvyvyvvyvyvvyvvyvyvvyvvyyo0000000.#%#%##%]]]]]~,~~,~!,~,~~~**{~*~****~~~~!~!~!!~!~!~!!''''''''',,&@@$>$@@@@@@@@@$$1111318111333377|7|||||||[|[[[[[[[<[<<< ", +" 000000e;f;f;;f;ff;fffffffffffggfgfggegfgggggdghggdhhdhdhdhhdddhddhhhdhhddcdhhdddhdddgddhdddddhddddddhdddhdhcddhddddddddddpdddddpddpdddddjdpdpdpdppdpppddpdjddpdodpppppdodopppppppopvpppvppppopvpvpvpvypypvpvvypvvypyvpyvvyvvvvvvvvyvvyvvvyvvyvyyyvyyoa000000)$#####-*]]~]*~,!~,~~!~,~~******~*{***~~~!~!~!~!!~!~~~,''''''''''!%@@@@@@@@@@@@@@$11$118181133333777|||||[|[|[[[[<[<[<<,,,!,'!~~~~~~~~'''',,*@@@@$@@@(////////////////////////',,,'!~~~~~~~''''',@@@@@@@> ", +" 000000c;;;;f;f;f;f;ffeffffffffegfgfggggggggggghghgdgdghdgdhdghhdgdhdhdhhhdddhdchddgdddddhdhdhdddhdhddddcdddddddddddddddddddddddddddddpdddddpdphoddppddodpddddopdppppppppppppppppppdppopppppppvvpvvvvpppvvpyvpppypvvpvvpvvvvvyvyvyvvyvyvyvyvyvyvvyyvyyyj000000b.##%#-==]]]]=~~,~!,~,~~~*****{*****~***~~!~!~!~!!~,~!~~,''''''''',~#@@$@@@@@@@@@@@$1111111$1123333|7|||||[||[[[[[[[<[<<<_////uxww/////n5n////&~'!,!,~~!~~~~~!'''',,&@@@@@@@@////////////////////////,,,,,,~~~~~~~'''',,@@@@@@@> ", +" 000000ef;f;;f;ff;fffffffeffgfgfgfggfgggggggggghgddhdhddhdhhddhdhddhhdhddhhhdhdhddhddhdhddddhddhddddddhdddhddddddhdddddddjdddddjddpddjddpdpddppdpdpdpppdpdodppdppppppppppppppppppopvppvpvpopvvpppopypvopypvpvypvvpypyvyvyvvypvvvvvyvyvvvyvyvvyvyvyvvyyoya000000+####====]*==*~!!~~,~,~**~*~*&@&~~**~~**~*~~!!~!~~!!~!!~!,'''''''','*+@@@$>@@@@@@@@$$111$//11823333|7|||||[|[|[[[[[<[<<///////////////////////,'~',,~~*~~~~''''',@@@@@$@> ", +" 000000a;;;f;f;f;fff;ffffffffgfffgggfgggggggdghgghhhhhhdghdhhhhhdhhdhhdhhddhdhdddgddddhdhdhdddhddhddhdddddddddddddddddddddhddddddddpdddddpdpdpdpppdppdpdppdpdppppdppdodpppppppopppdvvppppppvpvpvvppppyvpvppyppypyvovpypvvyvvvyvvyvvyvvyvyvvyvyvyvyyvyvyyoa000000b.##--===]=**=*~,!,~~~~******^/^.q~****~*~~~~!~!!!~!~!~!~!,,''''''''',-$@@@@@@@@@@@@@$^////^111113333||||||[|[[[[[[[[<<<<_////xwx(/////n55////#~',!!,~!~~~~~~!'''',,&@@@@@@@@///////////////////////'','',~~~~~~~,''',,@$@@@@@> ", +" 000000cf;f;f;f;ff;ffffffffgfgfggfgggggggggggghgdhdhdhdhddgdhddhdhdhdhhdhdhdhddgdddhhdhddddhdhdddddhdddhddddddddddddddddpddddddddpdddpddpddppdpdddodpdpppdpdppdodppppdppodpppdpppopvppvpopvpvpvvvvvvppvypypvpyppypvvvpvvvvvvvyvvyvyvvvyvyvyvvvyvyvyyvyvyyy000000b.##========*=*~!~~,~~*~****$/////&**~*~***~~~~~!~!~!~!!~~!!,''''',''''!%@@$>$@@@@@@@@@>////11312123333|||||||[[[[<[[[<<<<^///(s////////45n////*!,,,,,~~~~~~~~,''''',#@@$@@$@>//////////////////////,,'~'!~~~~~~~,'''',@>@@@@@> ", +" 000000e;;;f;f;ffffffffffffgfgfgefgfggggggghggghgdgdhhhhhdhdgdhchhdhdhdhdhddgddddhddhddhdhdddddhddddddddhddddhddddddddddddddddddpdpddddddpdddodppddpdpdppdodpppppppppppppppppvppppoppppppvpvpvpypppyvpvppvpyppypvvvvyvvyvyvvvvvvvvyvvyvvvvyvyvyvyvyvyyyyooa000000).*==-==-=*=*=*,~,~~*****~&///////^&***~~~***~~~~,~~~!~!!~~~~,''''''''',~#@@@@@@@@@@@@@@(//^11113112333|||[|[|[[[[<[<[<<@@@@+@(/////////////////////,,',',~~*~~~~''''',@$@@@@@> ", +" 000000a;f;f;fff;ffffffeffgfgfgfggggggggggggghhgddhdhhdhdhhchdhdhddgdhdhdhdhdddhdddhdddhdddhddhdddhddhdddddddddddddddddddddddjddpddddpdpdpdpdpdpddppdpppdppdppdpppppppppppppopppoppppopvpyppvpvpppyppvvvvypvpvvpyvyvpvyvvvvvvvvyyvyvyvyvyvyvvyvvyvyvyvvvyyyj000000)%]]===-=-*=*=*~!~,~*~r~**@/////////>m~**~~~~~~~~~,~,~~!!!!!~~!,''''''''''*.@@$>@@@@@@@@@>//^18112312233}||||[|[[[[[[<[<<<^/////////////n54////&!,'!'!~~~~~~~~,'''',,&@@@@@@@@/////////////////////',,,'!~~~~~~~''''',@@@@@$@> ", +" 00000ac;f;f;f;ff;ffffffffgfgfgfgfggggggghghggdgdgdgdhhdgddgddgdhddhdhhdcdhdhddgdhcdhddhdhddcdhdddhdddhddddhdddddhddddddddddddpdddpddpddppdpdpdjppdppddppdpppppppppdopppppppdpppppvpppvpppvpvpopyppvpypvpvpypyyppvvvvvvvyvvyvyyvvyvyvyvyvyvyyyyvyvyyyvyyvyyo0000000*]!]=-*-==*=*=*,~~******&////n///////$*~*~**~*~~~~~~!~!~~~~!~~~!,''''''''',-+@@$@@@@@@@@@+>/111111821223}3}|[|[[[[[[[<<<////////////////////,','',~~~~~~~,'''',@@@@@@@> ", +" 000b00l;f;f;fffffffffffgfefggggggggggggggdghgdhdhddhcdgddhddhddhhhdhddhdhddhdddddddhddddddhdddddddddhddddddddddddddddjdddddddddpdddpddpdpdpdpdddppdododppopppppppppppppopppppoppvpppvpvpyppoppvpypyppypypvvpvpypyvvovvyvvvvvvyvvvvvvvvvvvvvvyyvyvyvyvvyyyyya000000-]=!*=-=&=*=*=*~~~***~**^///_66n///////#*~~*~*~*~~~~~~!!!!!!!~!~~,'''''''''',-@@@@$@@@$@@@@@>111121131213}3}||[|[[[<[[<<<:_/////////////nn/////#,,,,!'~~~~~~~~!''''',-@@@@@@@@////////////////////,',,',~~~*~~~''''',@@@@@@@> ", +" 000000a;f;ff;f;ffffffgfffggfgffgfgggggghggghghhhhdgdhhddhhdgdhdhdhhdhhdhddgdddgdhdhdhdhdhddddhddhdhddddddddddhdddddddddddddddpddddjddpdpdppdpdppdpddpdpddpdppppppppppppdpppopppvppvvpvppppvvpypppypvpvppvpvypypyvpvvyvvvvvvyvyvyvyyyvyyvyyyyyvvyvyyvyvyyvoyyj000000c]]]*=-=--=*=***~*~****#////66666_//////^&*~*~~~~~~~~~!~!~!~!~!~!~~,''''''''',!%$>@@@@@@@$@@@>$11121131222322/2[[[[[[<[<<<///////////////////',,,',~~~~~~~,''',,$>$@@@@> ", +" 000000eff;f;ffffffffeffgfffgggggggggggggghghgdhdhhdhdhdgddgddgdhdhdhddgddhddgddddhdddddhddhddddhddddhddddddhdddddddddddddjddpdddddddppphjdpddppdpdppdpdopppppppppppppopppppppppvppppopvvpyppvvvypvyvypyypypvpypvvypypvyvvypvvvvyvvvyvvyvvyvyyyvyvvyyvyvyyyyoy0000000*]]=*-*-=-*==******~*&^///n6666666_//////^-~*~*~~~~~~~~!~!!~!!~~~~~!,'''''''''']#@@@@@@@@@@@@@$$1111212122///^}[[[[[<[<<<@@$@@@(//////////////////,'',,'*~~~~*~''''''@@@@@$@> ", +" 0000000lf;ff;fffffffffgffgggfgfggggggghghghghhdgdhdgdhhdgddhddhdhdcdhddhddddddhdhddhdhdddhddhdddddhdddddddddddddddddddddddddpdjdpdddphpppddpppdpdppdodpdppdppppppdoppdpppppppvppopyvvvyyyvyyyyyyyyyyyyyyyyyyyyyvyyvvvvvyvvvyvyvyvvyvyvyvyyvyvvyyyyyvyyyvyyvyyoa000000l]=**-*-=-==********~@///^666666666n^//////@**~~*~~~~~~~~!~!~~!!~!~~~~,''''''',,,*$@@@@@@>$@@>@>$$111121^/////$}[[[[[[<[<<<^////////////////////-',,,,,~~~~~~~~,'''',,#$@@@@@@@//////////////////','',,~~*~~~~,''',,@@@@@@@> ", +" 000000ae;ffffffffffffgfggffggfggggggggghgghgdhdhdgddhhdhdhdhhhdhdhdhdhddgdhdhddhddhdddhddhddddhddddddddhdddddddddddddddddddddddddpdpppdpdppddpdodpdpddppppppppppppppopppppovovyyyyyyyvvyyyvyvyvyyyvypyvvyvyvvyyyvyyyyyyvyvvyvyvyyvyvyvyvyvyvyvvyvyvyyyyyvyyyyyo000000)**=*=--*-*=*-&***~*&////n66666666666n///////$*~~~~~~~~~~~!~!~~!~!~!!!!!,''''''''',&+@@@$@@@@@@@>@$111^////////2}}[<[<<[<</////////////////,,,','~~~~~~~''''',@@@@@@@> ", +" 00000acf;f;fe;ffffegffefgfgegggggggghghghggdhhhhddgdhdgdhhddhhdhhddgdhdddhddhddhdddhdcdddddhdddddddddddddddddddddddddddddpdppdddpdddpdpdpddpdpdpdodppppdpppdpppppppppvvovyvyvyvyvvyvyyvyvyvyyyvvyvyyyyyyyyyyyvyvyyyvyyyyyyyyyvyvvvvvvvyvyvyvyyyvvyvvyvyvyvvyyyo000000a===**-*---=**&*9***(///_66666666666666n//////^#**~~~~~~~~~~,~~!~!~~!~~~!,''''''''',,&@@@>$@@@@@@>>@^//////////^2}}}[[[<<<:<_////(tww////////////#',!,,'~~~~~~~~~'''',,&@@@@@@@@/////////////////''',',~~~*~~~,''',,@@@@@@@> ", +" 000000effff;ffffefffffggfgggfgggggggghggdgdgddhdgdhdgddhdgdhdhhdhdhddhdhddhdcdddhdhddddhddddddhddhdcdddhddddddddjddddddpddpdddpddpdpdppddpodppdpddpdodpppppppppppopyyvyyvyvyvyvyyyvyyyyvyyyypyyyyvyvyyyyyyyyyvyyyvyyyyvyyvyyyyyyyyyyvvyvyvyvvvyyvyvzvyyyvyyvyyya000000-**=*=-*-*-***&***$////66666666666666666_//////>&~*~~~~~~~~~~,~~!~!~!!~~~~,''''''',',!#@@@@@@$@@$@@@@(////////^22k}k[<<<<<<////////////////,,',,,~*~~~~~''''',@@$>$@+> ", +" 000000aff;fffffffgffgfgfgfgfgggggggghgghgggdhdgdhdhhdhdhdddhhdhdhdhdhddhddgddhhddddhdhddhddhdcddddddddddddddddddddddphoddpdddpddpdpdpddodpdpdpdppodpdppppppppdopyyvvyvyvyyyvyyyyvyvyvyvyyyvyyvyyvyyyyyvyyvyvvyyvyyyyyvyyvyyyvvyvyyvyyyyvyvvyyyyyvyyvyyvyyvyyvyyyj000000e==***-=-=-*&*&***////n6666666666666666665^//////>=*~~~~~~~~~~~~~!~!~~!!~!~~,''''''''''*#>$@@@@@@@@@@@>////////^22kk}k[[< ", +" 000000ae;ffffffffffgfgfggggggggggghghghgdddhhhdhhdhdhhhdgdhdhdhdhdhdhdhdhddddddhdhddddhdddhddddhdhddddddddddddddddddddddddpddddpdppdpdpdpdpppdpddpdoppdppppovyvvvyyvyyvyvyvyvyvyvyyvyvyvyvyvyyvyyyvvvyypyyyyyyyvyyyvyvyyyyyyyyyyyyyyyyyyyzvyvvvyvyyvyyyvyyvyyvyyy000000)*=*=*=&=&--***&*$////666666666666666666666n^//////$*~~~~~~~~~~,~~~!~~~!~!!~~!,'''''''','-+@@@@@@$@@@@+@>///////_22kk}k<<_////////t(//////////////-,,,,,'~~~~~~~~,'''',,#@$>$@@@@///////////////,',,',~~~~~*~''''',@@@@@@$> ", +" 000000cff;ffffffgffgfgfgfggggggghgggghgggghddgdhhhdgdhddhdhdgdhdhdhddhddhddhhdddddhdhddddddddhdddddddddddcddddddddddpddjdddpdjhpdppdpdpdpdpdppppdpddppppvoypyvyyvyyvyyvyyvyvyvyyvyvyvyyvyvyyyvyyvyyyvyvyvyyvyvyyyvyyyyyyyvyyyyyvyyvyyyyyyyyvzyyvvzvyyvyyyyyvyyyyoa000000**==**=*-*&&*&*&(///n66666666666666666666666n///////#*~~~~~~~~~!~~!!!!~~~~!!~!!'''''''''',&@@@@@@@@@@@@@@(//////2222kk2//////////////////////////>!'!',,!~]~~~~~~'''',,~@@@@@@@@.#%&-%-==,',',','',,,~~~~~~~,''',,@@@@@@@> ", +" 000000g;ffffffeffgfgfggfgggggggggghghgdgdddgddhdhhdddhcdhhhddhdhdhdhddhddhddddhdhdddddhddhddhddddddddddddddddddddddddddddpddddpdpdpdpddpppdpdppdodpppoyyvvyyvyvvyvyvypyyvyyvyvyvyyvyvyvyvyvyyvyvyyvyyyyyyyyyoyyyyyyyoyyyyyvyyyyyyyyvyyyyyvzvyvyzvyvzvyyvyyvyyyyyya000000&**=**---&*&**&+>//_66666666666666666666666666_//////(&~~~~~~~~~~~~~~!!~~~~~~~~~,'''''''','!%@@@@@$@@@@@@@@(////^kk22kk///////////////////////////%,,,,,'~~~~~~~~!''''',&@@@@@@@+##%-&%-=*',''',','',~~*~~~~''''',@@@@@@@> ", +" 000000affffffffffegfgfgfggggggggghghgghghghdhhhdhchhhhdhdhdhdhdcdhddhdhdhdhhdhddddhdhdddddhdddddddddhddddddddddddddjpddpphpddpdpdpdpddppdppdppdpdppppyppyvyvyvyvyypyyyyvvvyvvyvyyyvyvyyyvyvyvyvyyvyyyvyyyvovyyyoyyvoyyvoyyoyoyvyyyyyyyyyyyyyyyyyyyyyvyvyvyyyyvyyyyj000000e==*=**&*---&=%>>//66666666666666666666666666665_//////>&~~~~~!~~~!~!~~!~~!~!~~!~~,'''''''',,*#@@@@@@@@$@@@@>(///_k_k22_///////////////////////////*',',,,~~~~~~~~,''',,,+@@$@@@@+%%%--&==!'',,',''',~~~*~~~,'''',>$@@@@@> ", +" 000000bffefffffgfgfgggggggggggghggghgdghddhhdhhhdhdhddhhdhhdhdhdhdhdcddhddddddhdhddcdddhdddddhdddddddddddddddddddddddddddjddpdpddpdjdppdpdppjdpppoyyyvyyvvyvyvyyvyyvvvyyyvyyyvyvvyyvyvvyyyyyvyyvyyyyovyyoyyyyvyvyoyyyyyyoyyyyyyvyyyyvyyyyyyyyyyyyyyyyyyyvyyyyyvyyyy000000)*=*=**-&*&*&*>@>(n666666666666666666666666666666n^//////.*~~!~~~,~~!~!!~!~!~!~!~~~!,''''''''',*+@@@$>@@@@@@@@>//(2k2n2k///////////////////////////>!,,',,!~*~~*~~!''''',*>@@@@$@@##%-%--==,'''',,'',~~~~~~~''''''@@@@@$@> ", +" 00000adfffffegfffffgfgfggggggggghghghghgdhdgddgdhdgdgdhdhdhhdhddgdddgdhdhdhdhdddhdddddddddhddddddddddddddddddjdddddddppdddddpddpdpddpdpppdpddppvyvvpyvvyvyvyvvvyvvyyvyvvyvvvyvyyvyvyvyvyvvvvyvyoyypyyyvoyvyvoyyoyyyyyovovyoyyoyyyyvyyyyvyvyyyyyyyyyyyyyzyyvyyyyyyyoa000000*=*=*=*--&*&#>@>(666666666666666666666666666666666n///////#~~~~~~~!~!~!~~~~~~~~!!~~!!'''''''',,,&+@@@@@@@@@$@@@>/^_2k2k_///////////////////////////&'!',,,~~~~~~~~,''',,,#@@@@@@@+#%%-%--=~,',''',',~~~*~~~,'''',@@@@@@@> ", +" 000000effffffffggfggfgggggggghghghghgdgdhhdhdgddgddddhdgdhddhdgdddhddddhdddddhdddhddhdhdddddhddhddhddddddddddhdpddddpddddpdpddppdpppdppdppdppypypyyvyvyvyvvyyvyvyvvyvyyyvyyvyvyvvyvyvyvyyyyvyyvyoyyyyoyyyyoyyoyoyovoyyyyyyyoyoyoyyyyoyyyyyyyoyyyyyyvyyvvyyyyyvyyyyyj000000&=*==-*&=--&@>>@n56666666666666666666666666666666666n//////(%~~!~~~~!~!~!!~!~!~~!~~~~~,''''''''',,&@@@@@$@@@@@@@@^2_2_kk^///////////////////////////,,',,,!~~~~~~~~''''',~@@@@@@@@.#%&%&-==~,,,,,!,!~~~~~*~''''',@@@@@@@> ", +" 000000aeffeffgfgfggfggeggggggggggghggdghhdhhdhdhdddgdhddhdcdhddddddhddhdhdhdhddhddhddddddddddddddddddddddddddddddpdddpdpdddpdpddppddppdpdpppyvyvyypypypyvyvvvvyvyvyyvyvyvvyvvyvvyyvyvyvyyvyoyovyyvyopyypyvoyvyyyyyyyyyoyoovoyyyyoyoyoyyyyoyyyyyyyoyyyyyyyyyyyyvyyyyyj000000e****&*-&=&#@@>$n5666666666666666666666666666666666665_//////>&!~~!~!~!~~!~!~~!~~~~~~!~~,''''''',,,~#@@@@@@@@@@@@@@_22_k2///////////////////////////.,!',,'~~~~~~~~!'''',,&@@@@@@@@##%%%%&&=~~~~~~~~~~~~~~~,''',,$>$@@@@> ", +" 0000000ffffgffgfgfggggdghdgdhddhdhddhhddhhhdgdhhdgddhdgdhdhdhdhdgdhddhddddhddhdddhdddhddhdddddddddddddddddddddpddddjhpdddpdpdppdpdppdpdppooyypypypypyyvyvyyyyvyvyvvvyvvyvyvyyvyvvyvyvyyvyyvyvyyvoypyoyooyyyoyyovooooyyoyyyyyooyyyyyyyoyoyyyyoyyoyyyoyyyyyyyyyyyyyyyyy000000b==**-*&*&&++>>k:n6666666666666666666666666666665666666n_//////@*~!~~,~~,~!~!~~!~!~~~~~~~!,'''''''',,*.@@@@@@$@$@@@@$_2_k_///////////////////////////~,,',,,~~~~~~~~,''',,,+@@$@@+@$##&#&%&&*~~~~~~~*~*~~~~''''',@@@@@$@> ", +" 00000ajffffffgfgfgfgghhdgdhdgdgdgdghhhdgdhddhhdhdhhhdddhdhdhdhdddddhdhdhddhddddddddddddddddddddddddddddddddddddddpddpddpdpdppdpdpddppdppyvpyvvypyvyyvvyyvvvvyyyyvyyvyyvvyyvyvyvyyvyyyyoyovoyoyoyyyoyoyyyooyvyoyoyyyyvoyyyoyoyyyoyyoyyyyyoyoyyyyyoyyyoyyyyyyyyyyyyyyyoa00000a**&**&*-&.$.+_k:k566666666666666656665655555565655656666n///////.~~~!~!~~,~!!~~~!~~~!~~~~!!,''''''',,,&+$@@@@@@@@@@@@$_2_///////////////////////////>',',',~~~~~~~~!'''',,&$>@@@@@@#$#&%&&--*~~~*~~~~~~~~~,'''',@@@@@@@> ", +" 000000cfffgfgfggggggghhdhdgdhhdddhddhhhdhchhhdhdhddhhhdhdhdhddgdgdhdhddddhddchdhdhdhddhcdhddddhdddddddddddddpddjdddpddddppdpdpdpdjdpdjypyyvyypyyvypvvyvvyyyvyvvyvyvyvvyvyyvyvyvvyovovyoyyyyvoyyooyoyypoyyyooyoyyyyoyyooyyoyooyyyoyyooooyoyoyooyyyoyyyyyoyoyyyyyyyyyyyj000000=**&*=*-%).++:}:n:6656555555555555555556565555655556565665n///////%~~,~!~!~,~,~!~!~~!!!~~~!~!''''''''',,&@@@@@@@@@@@@@@$k_///////////////////////////-,',',,]~~*~~~~,''''',#@@@@@@@+##&#&%&m**~~~~~~~~*~~~''''''@@@@@@@> ", +" 00000aggffefgffgefggggdghhdgdhhgdghhdhhdhhdhcdgdhhhdhhdhdhdhddddddcdddhdddhdddddddddhddddddddddddhdddddddddddpddddpdpdjhpdddjdpddppddpdppdppppdpppppppppppvpvppppppppvvvppvpvvvvpvvvppvpppvvvpvpvvvvpyvpvvyvyvvvvvvvyyyyvyyyyoyyyyyyyyyyyyyyyyoyyyyyoyyyyyyyyyyyyyzyyj000000&-*&*&*&.++.1nk&~~~,~!~!~!!!~~~~~~~~~~!~~,'''''''',,*&@@@@@@@@@@@>@@_^///////////////////////////,',,',!~~~~*~~~'''',,*$>$@@@@@$###&%&--*~~~*~*~~~~~~,''',,@@@@@@@> ", +" 000000gefgfgfggggggggdhdhdgddgddhddhhdgdhdgdhddhdhdhdhdhdhdddhddgdddhddhddddhddhddhdddhddddhddcdddddddddjddddddddpdddddppdpdddpppdppdppdppdpppppppppppvppppppvpvpppvpvpvvpvvpvvvvpvvvpvvvvvvvypvvvvyvvvvvvvvvyvvyyvvvvvyvvvyyvvvyvyyyyyyoyyoyyyooooyooyoyoyoyyyyyyyyyo000000e*&**-*++..$}}:}n<4565555555555555555555555555555555555556566n_//////@=~~!!!~,~~,!~~~~!~~~~~~~~~,'''''''',,*#@@@@@@@@$@@@@@///////////////////////////#,',',,~~~~~~~~!''''',%@@>$>+@@#$&%%&&-**~~~~~~*~~~~''''',@@$>$@@> ", +" 000000agfgfgfgfgfgggggdgdgddgdhhhdgdchddhhddddgdhdhdhdhdhdhdgdhddddhdhddhdhddhdddddddddddddddddddddddddddddddpddpdddpdppddppppddppdpppdpdpppppppppppppppppppppvpvpvvpvpppppvvvvvvvvvvvvvvvvvvvvpyvvpvvyvyvvyvvyvyvvyyyvyvvyyvvyyyvyvvyvyyyyoyoooyyyozyyozozozyoyyyyyyzo000000)**&*&&>+$)&2}:}<}n55555555555455554555555555555555555555555656n^//////.*~~,~!!,~!!!~~~!!~~!!~~~~~,''''''',,,*$@$@@@@@@@@@@@>//////////////////////////~',,'',~~~~*~~~''''',~)@@@@@@++##&#&%&-*~~~~~~~~*~~,''',,@@@@@@+> ", +" 000000aegffggfggggggggdhdhdgdgghghghghggggggghhghghgcghghedgdghgghgdgdedghghgdedgcghghhgdghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhdhhhdhhdhhhhhdhdhhhdhhhdhdhdhdhdhddhdhddddddhddddhddddddddddddddddddddddddddddddddddddpdppdddpdddpdppdpdpppopoooyozjyjyzjyooooozoyyyyyyy000000b*&**&+>@+$}}2}n}:}6555554545454454545444455454555555555555555555n///////#~!!~~~,~,!!~~~!~~~!~!~~~~!''''''',',,-@@@@@@@@$@@@@@>////////////////////////>,',,,'~~~~~~*~!''''''&@@@@@@$@$##&#&&-&~~*~*~~~~~~''''',@@@@@@@> ", +" 000000jffgfgfggggggggghhhhhdgfeffefffffegffefffefeffffeffffeffefeffefffffefefffffffefeflffeffffffeffflfflffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffflfffflffflffffffffffffffffffffffffffffffflfffffffffffffffffflffffoooyyyoyoyozzoyoyoyyyyyoa00000a&*-*#@>>.})}8<}k}k454545454544544545445545445454545455555555556555_//////(&!,!,~!~!,!!~~!~~~~~~~~~~~,''''''''',~&@@@@@@@@@@@@@@(///////////////////////=,'',',~~~~~~~~,'''',,#>$@@@@@+###&%&&-*~~~~~~~*~~,''',,@@@@@@@> ", +" 000000cgfgeggfggggggggdhhdgdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffflfflffflffffffflffffffffffffffffffffffffffffffffffffffffffffffffffffffffffflfflfflfffffffflfffflfffflffflffflfffflffffflffffffffflfffffflfflfffflfffyyoyjyoyozjozjzoozyyyyyza000000**&&@>@>13}32}}:}k:4545444544544444444444444544444544545455555555554_//////>&,~,~,~,~,!~~~~~~~~~~~~~~!,'''''''',,~#@@@@@@$@@@@@@>(/////////////////////(,,,',,!~*~~~*~~''''',*@@@@@@@@$#&#&%&-&~~~~*~~~*~''''',@@@@$@@> ", +" 00000adfgfggggggggghgggdhhdhgf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;l;;l;ll;;;;l;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ljazozoyjzoyyojzoyoyyyyyya000000&**#>>@>}3232}k:}kk44445444444444444444444444454444544444445454555555n^//////@*,~,~,!!,,,!!!~!!~~~~~~~~~,''''''',,,*$@@@@@@@@@@@@@>/////////////////////#,',',,~~*~~~~~,'''',,#@@@@@@@@##%#&%&&**~~~*~~~~,''',,@@$>@@@> ", +" 000000cfgfgfgggggggghgddhhhhgf;]!]~]~]!]~]]]]~]!]!]~]]]]!]!]!]!]!]]]]~]~]]]]]]~~]!]!]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]a0aoooooyozjyyooyoyyyyzyz00b000)*&>>>$n}238}&k}}}<5444444444444444444444444444444444445444445455555555n///////#~!~,~~,~,!!!~~~~~~~~~]~!~!,''''''''',-+@@@@@$@@@@@@@>////////////////////,',''',~~~~~~~~''''''!@@$>$@@@$##&#&%&-*~~~~~*~~''''',@@@@@@$> ", +" 000000agggggggggggggghghhhdhhff;]]]!]!]]]]]!]!]]]]]!]]!]]]]]]]!]]]]!]]]]]!]!]!]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]']]']]]']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]*aajoyzjzoooyjzozoyyyyyyyj000000e&$@>>$}}8}8}}}k}k}:44:444:4444:44:4:4:444:44:444444444444544444454555555n///////%~,,~!,!!,,!!!~~~~~~~!~!~~!''''''''',,&@@@@@@@@$@@@@@(//////////////////@,'',,'~~*~~*~~~'''',,&@>)@@@@@$##&%&&&-~*~*~~~~,''',,@@@@@@@> ", +" 0000000gegfggggggghghghdhhhdhgf;!]]]]]!]!]!]]]!]!]]]!]]!]]!]!]]]!]]]!]!]]]]]]]]!!]!]!]!]!]]!]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]']]]']]]]]]]]]]]]']]]']]]']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]']]]]]]']]']]']]']]]']]]]]]]]]]]]]]]]]a0joozjyzjyzoojyjzjzyyyyy000000)#>>@>k<}})}88}}}}}n4:44:44::4:4:4:4:4:4::4:4:44:44444444444454444445455454_//////>&,,~,,~~,!,!!~~~~~~~~~!~~~,'''''''',,~%@@@@@@@@@@@@@@(/////////////////=,'',',~~~~~~~~,''''',.@@>+@@++##&#&%&***~~*~*~''''',@@@@@@@> ", +" 000000jgfggggggggggghgghhhhdhgl;]]!]!]]!]]]!]]]!!!'!]'!!!!!!]'!!!'!!!!!!,!,!!']!!!!!!!!!!!']'!']']']']']']']']']']']']']']']']']']']']']'']'']]'']]']']']']']']']']']']']'']'']'']'']'']'']'']'']'']''']']']']'']]']]']]']']']]'']'']'']']']!]]*aajjyjzooyjyjzzoyyoyzyyyy000000b@@>>2}k}}8}28}}}}}}:4:4::4:4:::4::4:4::4::4:4::4::::4:4444444444445454455554^//////@=,~,,,!,~!,,~~~~~~~~~~~~~~,''''''',,,~$@@@@@@$@@@@@@>////////////////(,',',',*~~*~~~~''''',*@$@@@@@@$###&%&&-*~~~~~~,''',,@$@@@@+> ", +" 000000aggggggggghghgghhdhhhhhgf;]]!]]!]]!]]''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',!0aayzjyjzjyyjooozjyyyyzyya0000a0^@>@k}}<}388}&k}}}}4:4::4::::::::::::::::::::4::4:4:4::4:4:444444444454445455n^//////.*~,~,,,~,!,!~~]~!~~~~~~~~!,'''''',,,,*+@@@@@@@@@@@+@>//////////////.,,'''',~~~~~~~~,'''',,#@@@@@@+@##&#&#&*&~~~*~~''''',@@@@@@@> ", +" 000000jggggggghgggdghgghdhdhhg;;!]]!]!]!]!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0aaojoyoooozjzyjoyoyyyyyza00000a>@>k}}}}}}823}}}}}}<::::::::::::::::::::::::::::::::::4::4:4:444444444454554555n//////(#!~'~,,!,,,,!~~~~~~~~~~~~~!'''''''',,,&$@@>$@@@@@@@@@>//////////>-=*],,,'',~~~*~~~~,'''',~@$>$@@@@$##&%&&&-*~~~~~,'''',>$>$@@@> ", +" 000000cgggggggghghgghgdhhhhdhgf;]]']]]!]''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''a0jyoyjzoooooozjyzjzyyyyya000000>>$}}}}}}2&}&}3}}}}}:::::::::::::::::::::::::::::::4:::::4:4::4:44444444445445454_//////(&!,!,~,!,,,,~!!~~~~~~~~~~~,''''''''',,%+@@@@$@@@@@@@@(//////(%&&-===!','!~*~~~*~~!''''',&@)>@@@+@#$&#%&&&=*~*~~''''''@@@@@@$> ", +" 00000aoggggggghgghgdghgddhdhhgf;!]]!!]!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',,a0jjzyjojzyjzojzoooyyzyyza00000a>>k}}}}}}}}8283}}}}}:<:<:<:<:<:<:<<:<:<:<:<<:<:::::::::::::::4:::::4:4444444445455n_//////>=~'!,,!!,!,!~]!~~~~~~~~~~~,''''''',,,~#@$>@@@@@@@@@@>(///.&&-%---==*]~~~~~~~~~~~,'''',!$@@$@@@@+##&#%&&&*~~~~,'''',+@@@@@@> ", +" 000000cgggghghghggdgdhhhhhhhhgf;]!]!]'''''',''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaaojyzjyjzjozjoozjyyyyyyj000000>2}}}}}}}}&}&}8}}}}}::<<:<:<:<<<<:<<<<:<:<:<<::<:<:::::::::::::444:4:4:4444444445555n^//////+*!'~'!,!,,,~~~~~~~~~~~~~~~,'''''',,,,=.@@@$@@@@@@@@@>@%#%%--%------**~~~*~~~~~!''''''-@@>@@$>+)##&%%&&-*~*~''''',@@@@@@@> ", +" 00000aoggggghgghhghggghhhdhdhff;]']!]'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0aayjyjyjyjyjoyjzooyyyzyzj000000)}}}}}}}}}8}&}&}3}3}<:<<<<<<<<:<<<<<<<<<<<<:<<<<:<<<<<:::::::::::::::4:4:4444454444555n///////#~,!,!',,,,,!~~~~~~~~~~~~~!'''''''',,,&+@>$>$@@@@@@@@.#%#%%-&%&%&&-&=*~~~*~~~~,''''',#@$>@+>$@###%%&&&*~~~,''',,@$@@@@@> ", +" 000000cghghgghghghgdhdgdhhhhhgf;]]!]'','''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aaazjyjzjyjzozjozjyozyyyzj000000_}}}}}}}}}38383}}3}3<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<:<:<<<<:<::::::::4::4:4:44444444/^n554_///////%,'~,!,'!,,!~~~~~~~~!~~~~~!''''''''',,&+@@@@@$@@@@@@@.%#%%%%%&%%&-&-**~~~*~*~''''',~@@+@@@@@$##&#&%&-*~~''''',@>@@@$@> ", +" 000000dghggghghghhhghhhhdhdhhff;']!!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',a0jjyjyojzojojzojzooyyzozj0000002}}}}3}}3}}813&3}3}}}<<<<<<<}<<<<<<<<<<<<<<<<<<<<<<<<<<:<:<:<<:<::::::::4::4:4444_///_4554_//////>&'!,!,,',,,,~~~~~~~~]~~~~~,'''''''',,,#@@@@@@@@@@@@@+####&#&#&%&&&--**~~~~~!'''',,&@@@@@@@+###%%&&&-~~,'''',@$@@@@@> ", +" 000000cggghghghgdghhhghdhhhdhgf;]']'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aaaoozjyjyjzjyjojyjzyyyyyj000000}}}3}}}3}}88833}3}3}}<<<<}<<<[<}<[<[<}<[<[<<[<<<<<<<<<<<<<<<:<:<::::::::::4:4:44://////_555n_//////@*'!,!','!,,!~~~~~~~~~~~~~~,'''''''',,*#@@@@$@@$@@@@@+#$##&%%#&&&&-&***~~~~,'''',,+@@@@@@@$##&#&&&&*~''''',@@@@@@@> ", +" 00000adhghgghgdghghhhhhhdhhdhff;]]!'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0ajjojyjzjyjojzyjzooyzyzzj000000)}}}}3}}3}}}&3&33}33}<}<<[<[<[<[<[<[<[<}[<[<<[<[<[<[<<<<<<<<<<<:<<<<:::::::::4:4^////////n455n///////.~,,!,!',,,,!~~~~~~~~~~~~~!,''''''',,,-$@>@@@@@@@@$@++#$##&&#%%&&&-&=*~*~!''''',&@$>$@@@+$##&%%&&-~,'''',@@@@@@@> ", +" 000000hgggdghghhhhhhhghhhddhhgf;'']'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',a0ayjojyjyjyjyjzjzjyyyyzyz000000k}3}3}3}3}888}88}3}3}[[<[[[[<[[[[<}[<[<<[[[[[<[[<[<<[[<<<<<<<<<<<::<<::::::::::n//////////^n555n///////%~,,,,,'',,,!~~~~*~~~~~~~~!'''''''',,,&+@@@@@@@@@@@@++##$%&%#&%&&&-&**~~,''''',#@)>@@@@)$##&%&&-*,'''',@@@@$@@> ", +"000000bddgghgdghhghghhhhdhhhdhff;]!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aajjzjyjzjyjzjojjojyyzyozj000000)}3}3}3}3}}3&83&}3333[<[[<[[[[<[[[[[[[[[<[<[[[<[[<[[[<[[[<[<<<<<<<<<:<<::::::::////(////////_4554_//////(&',,,!,!',,,!~~~*~~~~~~~~~!'''''''',,~&+>$@@@@@@@@@@$+###&#&#%%&&-&-=~~''''',*>@$>$>@+$###%%&&-,''',,$@@@@@@> ", +"0000000dgghghgdgdhhhhghhhdhdhdgf;']''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''a0aoojyjoozjjozjyzjzyyzyyz000000_}}3}3}3}3383888}3}3}[[[[[[[[[[[[[[[[[[[[[[[[[[[[[<[<[<[<<[[<[<<<<<<<<<<::::::_////xwt////////_555_///////>=!',',!'!,,,~~~~~~~~~~~~~~,'''''''',,~#@@@@@@@$@@@@@+$###&%&#&%&&&--*!''''''%@@@@@@@+##&&#&&-~'''',@@@@@$@> ", +"000000adhghgdghhgcghhhhhdhdhhhgf;]!'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''~aaaojzjojojyjzjojjoozyyzyo00000j)}3}3}33}3}8888933373}[[[[[[[[[[[[[[[[[[[[[}[[[[[[[[[[[[[[<<[<[<[<<<<<<<<<<::$@@@@@@@@@@@+$####&%%%&&&-&=!'''',~+@@$@@@@$#$&%%%&=!'',,@@@@@@@> ", +"0000000hgdghhhhhhhhhghghhdhhdhgf;']''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''a0jzjjyjzjojojyjyyjyyzyyzy000000)[3}33}33}83888833}33|[[[[[[[|[|[[|[|[|[[[[[[[[[[[[[[<[[[[[<[<[<[<<<<<<<<<<<:^////twww////////////////////////#!,,','!',,,!~~*~~~~~~~~~~!'''''''',,,-+>$>+@@@@@@+@@)$###&#&%%&&&-=]'''',-@)>@@@@@)##&#&&=]''',@@@@@@@> ", +"00000b0dhghhghghhghhhhhhdhhdhhgl;'!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0aajzjyjojzojzjzjojyyyyyzo00000b)}3}333}9}3383&8933333[[|[|[|[[[|[[|[[|[||[|[[[[[[[[[[[[[[[[[[<[<[[[<<<<<<<:k/////((xu/////////////////////////(%,,!',,',,,,~~~~~~~~~~~~~~!''''''''',,&@@$>$@@@@>$@++$###%#&%#&%&&==!'',,$@@@@@@@$##%&#%==!'',>$@@@@@> ", +" 000000dgdghhhhhgdhghcghdhdhdhgf;]'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaajjyjzjojzjjjjzjyyyzyzoz0000002}33}333}333&38383333||[}|[|[||[||[||[|[|[|[|||[|[|[[|[[[[[[[[[[[<<[[<<<<<<<<_^//////////////////////////////////>%!,'!',',''!~~*~~~*~~*~~~~,'''''''',,!#@)>@@$@@@@@@@+$###&#&%#&%&==]!''*@@$>$>@@$##&#&%=='''@@@@@$@> ", +" 00000achhhhhghhhhhhhhhhhhdhhhgf;'!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0ajjzjjjzjjjoyjjzjjozyyzyj00000a)333}333333338&8933333|||[||[|[|||||[|||[|||[|[|[|[|[[[[[[[[[[[[[[[[<[[[<<<<<<<_///////////////////////////////////(-,,,,',,,',!~~~~~~~~~~~~~~,'''''''',,*#@@@@@@@@@@@@@+$####&#&%%%===],,#@@@@@@@+$##%%-==],,@@@@@@@> ", +" 000000dhhghhhhghhghghhhdhhddhgf;']''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aaajjoojjyzjzjzjjzzozyyyzo000000}}3333333333&388833337[|[||[|||[|||||[||||[|||||[|[||||[|[[[[[[[[[[[[<<[[<[<<<<*,,',',',,,!~~~~*~~*~~~~~!,''''''',,,*+@@@@@$>+@@@+@+##$&%#&&#--===!~+@@@@@@@+##&#&-==,,@@$>@@@> ", +" 000000chhhhghhhhhhhhhhhhdhdhhgf;!'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0aaojzjyjjjjjjojjjjyyyzyzj0000008}333|3333338m8m939333||||||||||||||||||||||[|||||||[|[||[|||[[[[[[[[[[[<[<[<[<<<@@@@@@@@@$+$#####&%&%--===&@+@@@@@+$##&%--=~,@@@$@@+> ", +" 000000dhhgchhghghhghhhhhdhdhdgf;'!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''a0aojjjzjyjyzjzjooozyzyyyj000000}83333333333988&333939[|||||||||||||||||||||||||||[||||[||[[|[|[}[[[[[[[[[<[<<<<<<<$@@>+@@$###&#&%----*-+>@$>+@@+##&%--==!@@@@@@@> ", +" 000000cdhhhhhhhhhhhhghhhdhdhhgl;',''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aajjoojjjojjjjjjjzjyyyzyzj00000j333333333333m898993739||||||||||||||7|||||||||||||||||||[|||}|[|[|[[[[[[[[[<[[<[<<<<<<_///////(wwws////////////////////////&,',',,,',,,~~~~~~~~~*~~~~,''''''',,,~)@@>)@@@+@@@@++$###%%%-&-&*&>>@@@@@+$##--%-**@@@@@@@> ", +" 000000jhgdghghhghghhhhhhdhdhdgf;']''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',a0azjjzjzjzjojzjjjjzyzyzzj000000333333939333m8&8939393|7|77|7|77|77||77|77|7||||||||||||||[|||[|[|[|[[[[[[[[<[<[<<<<<<<<_///////tx//////////////////////////(*,',',''',,!~~*~~*~~~~~~~~,''''''',''*+$>@@@@@@@@@+@$####&-%--&-+>@>$>$@@##&----*>@@@@$@> ", +" 00000acdhghhhhhhhhhghhhhdhdhhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0aajzjjjjjjjzjjzjzoyzyzyzj00000a3339333339338m3&893939|7|7|77|777|777|77|7|777|77||||||||||||||||[|[|[[[[[[[[[[<[<<<<<<<:k^///////////////////////////////////@]''',,,''''!~~~~~*~~*~~~~!'''''''',,,-$@@@@@@@@@@@@@$###%-%--&&>>>>>@@@+##-%-&*@>@@@@@> ", +" 000000adgdhhgchhghhchhhdhdhdhgf;]'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaajjjzjzjzjjzjjjjjzyyzyz00000003333337939398m8m893939|7|7|77|7|7|7|7|7|777|7|7||77|7||||||||[|||[|[|[|[[[[[[[[[[[<<<<<<<::^////////////////////////////////////.,''',',,',,~~~~~~~~~~~*~~!'''''''''',&@@$>$@@@@@@@@@+##%%--&-#>>@>>@$@##----->@$>@@@> ", +" 00000aahhghhhghhhghhhghdhdhdhgf;,',''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0jjjjjjjjjjjjojzoozyzyzza00000a33393933739389m8m39i397777777777777777777|7777777|77|77|7||||||||||||[|[|[[[[[[[<[[[[<<<<$@@@@@@@@@###%%&&&@^>>>>>>+#%%&-&>>>$@@@> ", +" 000000ahhhhghhhhhhghhhhhdddhhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaajyjojzjozjjzjjjzyyyzyz000000b337393939393m8mm893993777|7|77777777777777|77|7777|77|7|77||7||||||[|||[|[|[[[[[[<<[<[<<<<^////////////////////////////////////////(-',,,',',~~~~~*~~~~~~*~*~~,'''''',,''*#@@@>$>$@@+@@@+#%%%-&#>>>>>@@>##%--&>@>@@@+> ", +" 000000ahhhhhhghhhhhhhhhdhhddhgf;!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',,0aajjzjjjjjjjjjzjjozyzyyza00000b9393393939398m8mm899397777777777777777777777777|77777777|777||||||||||[||}^}[[[[[[[[[<[<<_///////////////////////////////////////////-''',',~*~~~~~~*~~~~~~~~~~!,'''''''',,&++>)@@@@@@@@+@.#)%%&+>>^>>>>@#%%-&>>@>@@@> ", +" 0000000dhhhhhhhghhhghhhhdddhhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaajjjjzjjzjzjjjjzozyzyzy000000)}939393979939m8m8939987777777777777777777777777777|7|777|7|7777||||||||||^//$}[[[[[<[<[<[///////////////////////////////////////////(,'','',~~~*~~~~~~*~*~*~*~~~~!''''''''',,&+>@@@$>$@@@@++.%%%%>>>>>>>>+.#-&>>>>$@@> ", +" 0b0000bhdghghhhhhghhghhhdhddhgl;','''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0jojjjjjjjjjjjjjjzzyzyzz00000b)93939393939898mmm9939977777777777777777777777777777777777777||77|7||||||2/////1}[[[[[[[[<2//////////////////////////////////////////-','',,~~~~~*~~~~~~~~~~~~*~~*~~!'''''''',,~&@@@>)@@@@@@@@++&#+>>>^>>>>.#)&>>>>>@@> ", +" 000000dhhhhghghhhhhhhhdhdhdhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aaajjzjjjzjjjzjzjjyyzyyzj0000002379397993i399m88m89939i77i777i77i7i777i77i77i777777777777|7777777|7|||||////////2[[[[[<[<<<_///////////////////////////////////////>,','''~~~*~~~~*~~~~~*~~~~~*~~~~~~,'''''''',,*#@@@>@@@@@@@@@+##>^>>>>>>>##%^>>>@@$> ", +" 000000chhhhhhhhghhgchhdddhdhgf;','''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0jjjazjjazjjjjjjjyzyzyzj00000a83939393999998mmm9999997i777i77i77i7i7i797i7777i7i771$377777777|7|77|7||$/////////^2[[[[[[[[>>>>>>>@+#>^>^>>@> ", +" 000000jdhghhghhhhhhhhhhdhddhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aaajjjjjjjjjjjjjzzyzyyzya0000008939939939393m8mm8999987i7i7i7i7i7i7i7i777iii7i77i78///17777777777|77|73////////////_}[[[<[[<<$@@@@@@@@>^>^>^>>>++(>>>>>@> ", +" 000000adhhhchhhhhghhhdddhddhgl;'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0azjjjjjjjjzjjjjjyyzyzz000000b9793999399999m8mmm98999i7i7i7i7i7i7i7i7iii77i7i7i77^////^17777777777|7|^/////////////(_[[[[<[[<<$>$@@+@+@>(>>>>^@+(/(^>>>> ", +" 000000ahhhghghghhhhhhdhddhddgf;',''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaajjzjjzjjjjjzajzzyyzyz000000b3939399793998mmm8m999997i7iiii7iii7i7i7i7ii7i7i7ii8///////^377777777772/////////////////2[[[<[<[<<<_//////////////////////////////-''',',~~~~~~~~*~*~*~~~~~~~*~*~*~~~~~*~*~~~,'''''''',,~#@@@)>@@@@@@@@@>(^>>>+^(>((>>^/ ", +" 0000000dhhhhhhhhghhhdchhphdhgf;'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0ajjjjjazjjjjjjjyyzyzyz00000b)3999i89899999m8mmm99999ii7i7i7ii7iiiiiiii7iiiii7i7^/////////$377777777///////////////////^k[[<[<[<<<<^///////////////////////////>',,'',!~~~*~*~~~~~~~~*~~~~~~~~~~~*~*~~~~~~*~~,'''''',,,,*$@@+>$@@@@@@@+>>>(>>(/((^(>(/ ", +" 0000000cdghghhhhhghhdddddddhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',''aaajjjjzajjjjjjzjzyzzyzo000000$79398i9993999mmm8m899997iiiiiiiiiiiii7iiii7i7iiii$////////////$7777771/////////////////////^}[[<[<<$>+@@@@@@>^>(((^(((>(// ", +" 00b000ddhhhghhghhhhhdhdhddhgf;'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0azjjjjjjjzjjjjjyzyzyzj00000088979893999998mmmmm99999iiii7ii7iii7iiiiiiiiiii7i3//////u(///////17773////////////uwt/////////_}[<[<<_///////////////////////////.,'',,'~~~~~~~~*~*~~*~~~~~~~~~~~~~*~~*~~~~*~~*~*~~!''''''''',,&+@@)>$@@@@@@@>>^>/(((^((// ", +" 000000cdhhhhhhchghhdhdddhddgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',''aaaajjjjjjjjjzajzzyzyzzj00000089999999999999m8mmm99999i7iiiiiiiiiiiii7iiiiiiiii$/////(wwt///////^87^////////////xwww//////////2<[[k////////////////////////////,,'''',~~*~~*~~~~~~~~~*~*~~*~*~*~~~*~~~*~~~~~*~~*~*~!'''''''',,~&>@@@>$@@@@+@>>((/(((((/// ", +" 000000adhhhghhghhhhddhdhphhgf;'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0ajjjjazjjjjjjjyyzyzyzj00000a9938i98i989999mmmmm99m99iiiiiiiiiiiiiiiiiiiiiiii8/////(xwww(////////^////////////uwwwt////////////k<^///////////////////////////-'',',,~~~~~~~~~~~~~*~~~~~~~~~~~~*~~~*~~~~*~*~~*~~~~~~~,''''''',,,~#@@@@@@@@@@@^>>^((^(//// ", +" 000000ahdghhhhhhhhdhddddhddgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',''aaajjzjjjjjjjjjjzyzyzzz000000b39999999999999mmmmm99999iiiiiiiiiiiiiiiiiiiiiiii^//////xwwt///////////////////////(xx//////////////////////////////////////////(,,'''',~~~~~~~*~~*~~~~~~~~*~~~*~~~~~~~*~*~~~~~~~**~*~*~~~,'''''''',,*+@@@$>@@@@>(((/((((/// ", +" 0000000ddhhhhghhhhdddhddhdhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0a0jajjjjjjjzjjjzzyzyyz000000)97989999999998&mmmm9qqq9iiiiiiiiiiiiiiiiiiiiiiii8^//////>t////xx(//////////////////////////////////////////////////////////////]'',,',~~~~~~~~~~~~~~~*~*~~~*~~~~*~*~*~~~~~~~*~*~~~*~~~~*~~!''''''',,,,&+@@$@@@@>>>>((^(/////", +" 0000000cdhhghhhghhhdhjhddddgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaajjjjjz0zajjjzyzyzzyj000000299999i899999m9mmmmm99999iiiiiiiiiriiririiiiiiiiiii1(/////////uwwwu////////////////////////////////////////////////////////////+,,''''~~~*~~*~~*~~~*~~~~~~~~~~~*~~~~~~*~*~*~~~~~*~~~*~*~~**~~!'''''''',,~+@>+@@+>^>(((((/////", +" 000000adhhhhhhhhhddhddhddhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0aajjjjjjjajjjayzyzyzza00000098999999999999mmmmmm9mqq9iririririirriiiririiririrrii$////////xwww(////////////////////////////////////////////////////////////,'''',,~~~~~~~~~~*~~~~~~~*~~~*~~~~~~*~~~~~~~~*~*~~*~*~~~*~~~**~~,''''''',,&@@@@@++(>^(/^(/////", +" 000000adhhhghghhhdddhdddhdgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''a0ajjz0zajjjjjjzzzzyzz000000b99999999999m99mm&mmm999qriiiiiriririrriirirriiiriiiiii9$///////tws///(u///////////////////////////////////////////////////////%,',','~~~~~~~~~~~~~~~*~~~~~~~~~*~*~~~*~~*~*~~~~~*~~~*~~~*~*~~~**~~,'''''',&@@@@@@@>(>(((//////", +" 0000000ddchhhhhhdhddhddddhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aa0jajjjjjjjjjjzyyzyzz000000)998999999999m9mmmmmmqqqmirirrriririirirririiriririririii9^///////////xwx(////////////////////////////////////////////////////(,,'','!~~~~~~~~~~~~*~~~*~~~*~~~~~~~*~~~*~~~~~~*~~~~*~~*~*~~~~~*~~*~!''''''!)$>$@@@@^(>((///////", +" 000000cdhghhghhhddhjhddddhhgl;eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeea0jjjjjjjjzazazzzzyzya00000029i999999qm99q9mmm&mq9q9qiriririrrrrirririrrrririririiiiiir8/////////uwwwt////////////////////////////////////////////////////*','','~~~*~~*~~~~~~~~~~~~*~~~*~~~~~~*~~~~*~~*~~*~*~~*~~~*~*~*~*~~*~,''''''-@@@@@@@>>>^(////////", +" 000000jddhchhhhddhdhddhdhddabaa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aaajjjjjajaajayyzyzyza0000009999999m9999q9m&mmmm9mqqmririrrririrrirrrrririrrirrirrrrriiii1///////uwww////////////////////////////////////////////////////@',',''~~~~~~~~~~~~~*~*~~~~~~~~~~*~~*~~~*~~~*~~~~~~~*~~~*~~*~~~*~~*~!''''''!@@@@@@@+>((>>////////", +" 000000ahdhhghhhhddddhdddddhjaaaaaacaaaaacaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0aaa0ajjjjjjjjjjjzyzyzzz000000b999999999qq999qmmmmmq99q9rrrriirrrrirrririrrrrrrrirriiiiririri9$///////su///(wu//////////////////////////////////////////////!''',',~~~~~~~~~*~~~~~~~*~~~~*~~~~~~~~*~~*~~~*~*~~~~~*~~*~~~*~~~*~*~''''','#@@@@$@@@^>>(/////////", +" 0000000dhhhhgchddhdddddhdddhdhdhddphdhdpdddhddpddddhddddddddddddddddddddddvovovvoopovooopooooooooooooooooojoojooojoojojojjojjojjjjjjjjjjjjjjjjjjjjjjajjajjajajajajajaajaajaajaajaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajaajajjjjj0zajjjjjzzyzyzj000000$999999q9q99mqqmmm&mmqqqqqirirrrrirrrrrrrrrrrririrrirrrrririririi9^//////////xwwx////////////////////////////////////////////#',',''~~~~~~~~~~~~~~~~~~~~*~~~*~~~~*~~~~~~*~~~~~~*~*~~~*~~*~~**~~*~,''''''*@$@@@@@@>>(^(/////////", +" 000000jdhhhhhhdhddhdhddddddddddddddddddddddddddddpdddddddddpddddddoddodppvvvvvvvvvovvvovvvvvvopopovopoovopopooopoooodjpoopjoopjdododojdjojpjojojdjjpjojdjojdjjojjjjdjjjjojjjjjjjjjjojjjjjjjjjjjjjjjjjjjjjjjjjjajjajajjajjjjjjjajjjjjjjjjjjjjaajjjjajjjjjzzzyzzzj0000008999m99m9q9q99qmmmmmmqqqqqirrrrrrrrrrrirrrrrrrrrrrrrrirrririririiri8(///////uwwwt///////////////////////////////////////////(,''',',~~~~~~*~~~~~~~~*~~~~~~~~~~*~~~~*~*~~~~*~*~~~~~*~~*~~*~~~~*~~~'''''''.@@@@@@$>>(>(///////// ", +" 000000addhhhhhddddddddhddddddddddddpddddddpddpdddpdddddpppddpodpppdpdpppvvyyyvyvyvvvvyvoyooovovoovooopoooooopooojpoooojpjoooojoojjjojojdoojojdjjjjojjjjjjjjjdjjjdjjadjjjjjjjjjjdjjjjjjjjjjjjjajjjjajajajajajajjajjjjajjjajajajjjaajaajajajajjajajjajajajzyzyzyz0000000m9999q99m99q9mqmm-mmmqq9qrrrrirrrirrrrrrrrrrrrrrrrrrrrrirrrririrriiii1///////uxw////////////////////////////////////////////=',',',~~~~~~~~~~~~*~~~~~*~~*~~~~~~~~~~~~~~~*~~~~~~*~~~*~~*~~*~*~~*~,''''''&@@@@$>)@>^(>////////// ", +" 000000ahdhhghhphddhdddddddddddddddddddddddddodddddoddoddpppddpddpdppdpdpyvvvvvovyovyovypyvvovovvovooyovpooooooopooooooooopjpjojdjpojdjjjojdjjjoodjjdjdjojdjjjjdajjjjjjjdajdjjjjajjjjjjjjjcjjjjjjjcoajjjjjjjajjajjjcjjjajajajajjjjajjjjjajjjjjjjjjjjjjjjzzyzzzzy000000b99m99q9q9q9q9qqmmmm-qqqqqrrrrrrrrrrrrrrrrrrrrrrirrirrrrririrrriirrriri9$///////(///////////////////////////////////////////>,''','~~~~~~~~~~~~~~~~~~~~~~~~*~~*~~*~~~*~*~~*~*~*~~~*~~~~~*~~~*~~~~'''''',+@@@@@@@>>>(////////// ", +" 0000000dddhhhhdhddddhddddddddddddddddddddddddddpdddpddpdddpdpdppppdpdppvvyvyvyvvvvyovyvovoovovoyovovoooopooopooojooopjpjojojpoojojojooojojojdjjjojjjjjjjjjjjjjjjdjjjjjjjjjajjjjjjjjcjjcjjjjjjcjajjjjcjajcjjjjjjajjjjjajjjjjjjjajjjjjjajjajjajjj0jjjjajazyzzozzj00000a$999999q9q9qqqqqm-mmmqqqqmrrrrrrrrrrrrrrrrrrrrrrrrrrrirrrrrrrirrriiiriiii8$/////////////////////////////////////////////////!',,'',~~~~~~~~~*~~~~~~~~~*~~~~~~~~~~~*~~~~~~~~~~~~~*~~~~**~~~*~~*~~,''''',%@@@$@@@@>^(////////// ", +" 000000adhdhhddddddddddhdddddddddddddddddodddpdddpdppdpdpdodpdpdpppppdpovyvvvyvoyvvvooyvyvyyovovooovoopooopoooopooojoooooopjjjpoojojojpjojdjjojdaoojojdjjojdjjjjjjdjjcojjjdjcojcojjjjjjjjjjjjjjjjajjjjjjjajcjajjajajajajajjajjajaaaajjjajjajj0zjjaj0zajzzyzzzy00000008q9qm99q9q9q9m9qmmm-mqmqqqrrrrrrrrrrrrrrrrrrrrrrrrr8qrrrrrrrrriirrririiiiii8^//////////////////////////////////////////////#,''','~~~~~~~~~~~~~~~~*~~~~~*~*~~~~*~~~~*~*~*~~*~*~~~*~*~~~*~~~*~~~,''''''*@@@@@@@@^>/////////// ", +" 0000000ddhhhdddhdhdddddddddhddddddjdddddddddodddpdpdpdpddpdpdppddodpppvyvvyvvyvvyoyvypovoovvooovovoooooooooopoooopoooooojjpoojojpjpjojojjojdjjjojjdjjjjdajjjdjjjjjjjocjjjjjjjjjjjjjjjjjjjjajjcjjjjjajjajjjjjjajajajajjajajjajjajjjjaajajjajjjajjjjjaazzyzzzyz000000b99999qq9mqq9qqqq-mmmmqqqqqrrrrrrrrrrrrrrrrrrrrrrrrq/($irrirrrrrriririrriiriir1(///////////////////////////////////////////(,''''',~~~~~~~~~~~~*~~~~~~~~~~~~~*~~~~~~~~~~~~*~~~~*~~~~~~*~~**~~*~~,''''',#@@)$@@+@/////////// ", +" 0000000cddhhhddddddddddhddddddddddhddodddpdddddpddpdpdodpppodpdodpdppvvyvyvyvvovvvovyyovvooovvovoovpopovooooojpjojpjopjpjoojpjpjojojpjdjjojjjojjdjjjdjjjjjjjjjjjjjjjjjjjjjjjjjjjcjjjjcjajjjjjjjcjjajjajjajjajjjjjjajajajaajajjajjajjjjjajjjajajajajjjyzzozzza000000)im9q99q99qmq9qmqmm-mqqqqqqirrrrrrrrrrrrrrrrrrrrrrr>///^8{rrirrrrrririrriiiiiii8///////////////////////////////////////////-,,,,',!~~~~~~~~~~~~~~~~~~*~*~~~~~~~~~*~~~~~*~~~~*~~~~*~~~~~*~~~*~~~,''''''-@@$>@@@@^////////// ", +" 000000addhhdhdddddddddddddddjddddpdddddpddddddpddppddpdpddpdpdpdppdpyyvvyvovyvoyyyvovvoovyvooyovooooooooooooojpojpjojojpjpjojjpjojojjojojojjdjjjjjjjjjjdjjjadjjjdjjjjjocjjjcjjjocjjjjjjjcjajjjjjjjjjajjajjcjajaajajjajjajjjaajaajaajajajajajajjjajajzyzzzyz000000b999q9mq9qqq9qqqqm-mmmmqqqqrrrrrrrrr{r{r{{r{r{r{rr{8//////^9rrrrrrirrrrrirririii^//////////////////////////////////////////>''''''!~~~~~~~~~*~~~~~~~~~~~~~*~~*~*~~~~~*~~~~~*~~~~*~~*~*~~~~*~~~~~'''''',+@@@@@$@>////////// ", +" 0000000dddhdddddddddhddddddddddddpddpdddddoddpdpdddodppdppppppppdodvvvvyvyvvyyvvyovoovvovopoypooopoopoopoooopoooooopjpjojoodjojojdjojdjjdjjojjojjdjjdjajjdjjajjcjjjjjjjjjcojjjjjjjajjjcojjcjajajajcjjajcjjjjajjjajaajajaajajajajajjajajjjajajj0zaajzzyzyzzj000000)m9999q9q9qqqqqqqmm-m=qqqqq{rr{r{r{r{r{rr{r{r{r{rrr(////////$9rrrrrriirriiririi8///////////////////////////////////////////],'',,,~~~~~~~~~~~~~~~~*~~~~~~~~~~~~~~*~~~~~*~*~~~*~~~~~~~~*~*~~**~~,''''''%@@@@@@@@///////// ", +" 000000adddhddddddddddddddddddddddpdddjddddpddppdppdpdppddpddpdppdpvvyvvyvovvovovvovyooyoyoooyovooooojpjpjpooojpjoojojpjpjjojdjojjdjjjojjdjjjjdjjjjjjjdjjjjjojjjadjjjjjjjjjjjajajjjjajajajjjjajjajjjjajjjaaajaajjajajajajajajjajajajjj0jajjajjaajjazzzzzzza000000&9mq9q9q9mq99mqqq-qmmmqqqqqr{rr{r{r{rrr{{r{r{r{r{{$///////////$rrrrrrrirrririr9///////////////////////////////////////////#'','''~~~~~~~~~~~~~*~~~~~~~~~~~*~~~~~~~~~*~~~~~~*~~~~*~*~*~~~~~~~~*,''''''~@+@$@+@@>////// ", +" 0000000hdddddhddhjdhddddddddddddddpddddpddpdpdppddpdpdpppdododpvpvyvyvyvyvyvyvoyvovvvopovovopoopoopooooooooooooopjpjpjjpjojojojojjoojdjjjjdjjjjdjjdajjjjjjcjjjojajcjjcjjjajjjjjjcjjjjjjjjajajjcjajcjaajajjajajaajaajajajajajajaaajaajjjajjj0jjaajzzzjzyzz0000000399q9qmq9qqqqqqqmmm-mmqqqqqr{rrrr{r{{{rr{r{r{r{rrq/////////////^8rrrrrrirririi$///////////////////////////////////////////,,'''',~~~~~~~~~~~~~~~~~~~~~~~*~~~*~*~~~*~~~~~*~~~~*~~~~~~~~*~~*~*~~,''''',#@@@@@@@@////// ", +" 0000000jdhddddddhdddddddddddddjddpddddpddddpdpddppppppdodpdpdppdpvvvvyvvvovyoyvovyooyyovooooooooooopooopjpjpojpjoojojoojooojdjojdjojjjojojjjdjjjjjjjjjjcojoocjjjjjojjojjjjcjjcjjjajcjaajjcjjajjjajjjjaajajajcjajajajaajajaaaajajajjjajj0ajjjajaj0yyzzyzzj000000)9q99q9q9q9mqqqqqq-mq-q*qqqr{rr{{{r{rrr{{r{r{r{r{{$///////////////>8rrrirririr9///////////////////////////////////////////-,'',,,~~~~~~~~~~!~~~~~~~~~*~~~~~~~~~~*~~~~~*~~~*~*~~~*~~*~*~~~*~~~~,''''',=@+@+@@$@(///// ", +" 000000ahpddddddddddcdpdddddddddddpdpdddodpdpdodpdpddpddppppppvppyvyvvyvyvvyvovyvovopoovovvovopyooooooooooojpjoojpodjpjpjjpjjojojjdjojojdajjjjjdjjjdjjojjjaojjjjjajcjjajjjjajjajjjjjjajjjjjajajaajaajjjajajjjajajaajajaajjjajajajaaajajjjajjjajjzzyzzyzz000000089m99q9qqqq9qqmqmmm-mmqqqqqr{{rrr{r{{r{r{r{r{r{rrm//////////////////$9rrrrrrii>//////////////////////////////////////////>,',''',~~~~~~~~~~~~~~~~*~~~~~~~~~*~~~~~~~~~~~~~~~~~*~~~*~~~~*~~~*~*,,''''',+@@$@@@@>//// ", +" 0000000hjhddddddddpddddddpddddpdddpdpddddppddpdpdpdodpdpdpppppppvyvyvoyvyovyvovovyyovooyooooooopooopjoojoooojpjojojojojojoojdjojjjdadjjjpjjdjajjjjjjjjocjjjjjocjjjjjocjjjjjjjcjjaajjcjajajcjjajjcjaajajajaaajjaajaajajaaaajajaajajajajajaaajaaayzzyzzyz000000bq99qq9q9mqqqqqqqq-mq-mqq*qq{r{r{{rrr{r{rr{r{r{r{{^////////////u///////$ririrr8///////////////////////////////////////////*''''',~]!~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~~*~~~*~~~~~~~~*~~~~*~~~*~~~~,''''''%$@@@+@@+///// ", +" 0000000jhddddddddddddddddddppdddjddpdpdpddpdppdppdpdpdodpppppppovvyvvvyvvoyovyvoovooypypovovooooooooopjpjooooojdjopjpjpjjpjojjodjjjjjjojjjjjjdjjdajjdajojjjjcjjjjjjajjjcjjcjjjajjcjjjajajjjaajajajajajcjajjaaajajajaajjajaajajajaajaajaajajjjazzzzyzzza000000)999qm9qq99qqqqqqmm-mmqqqqq{rr{rr{{{{~~~{!{{{{{rrm////////////uwwu///////8rrr9///////////////////////////////////////////+,,',''~~~~~~~~~~~~~~~~~~~~*~~~~~~~~~~*~~~~~*~~~*~~*~*~~*~*~~~*~~~*~,''''''!@@@@@@@$>//// ", +" 0000000ddddhdddddddddddddddpddddddpdpddopdpdppdpdpdpdpppdppdppvyvyoyovoyvvyopovyopovjyoovjpopoopoopooopjpjpjooojojojjjpjojjdjjjjjojdjjdjocjjjjajojajjjjcjjjjocjjcjjjjjjajjjajjajjajcjajaajajajajajaajjaaajajaajaaajaaajajajaajajaajajaajaj0jajzyzzzyz000000099q99qmqqqqmqqqqq-mq-m=qqqq{r{{{{~,!'~,',,!'!!{{{{m(//////////wwww////////(8r$///////////////////////////////////////////,',',',~~~!~~~~~~~~~~~~~~~~~~~~*~~~~~~~~~*~~~~~~~~~~~~~~~~~~*~~~~~~~,''''',#+@+@$@@@///// ", +" 0000000jhddddddddpdddjhpdddddpddddpdppddpdppdpdodododpppppvppvvvyvvyvyvyovvoyyovovypypyooooooooojojoojoooojpjpjopjoopjojojojjpjpjjjjjjjjoojjdjjjjcojjjojjjjjjjjjjjcjajjjajajjcjjajjjajcjjacjajaajajaajajaajaajaajaajaaajaaaaajaaajaaajaajajazzzyzyzza000000)89qq999qqqqqqmqqqm-q-qqqqqq{r{{,,!!!!!~!!!!~!,!!{{rr8////////(xwwu//////////^(//////////////////////////////////////////%'',','~~~~~~~~~~~~~~~~~~~~~~~*~~~~~*~*~~~~~~~~*~~*~~*~~*~*~~~~*~*~~,''''''=@+@@@@+@(//// ", +" 0000000ddddddddddddddddjddpddpdpddpddppdodpdpddpddppdppppdppvvyvyvvyvoyvoyopoyopyjoooovovoopooopopooopjoojojoojjdjjojjdjdjojjjjpjjdjjdjjjjjadjjojjcjjjjcjjjjjjjjjjdajcjjjjajjajjajaajjajjjjcajcjajaaajjaaaaajaajaaaajaaajajaajaaajaajajaaaazyzzyzzz00000008mq99qqqq9qqqqqqqmqmmm-q=qqq{~!!!~,~,!,,!,~,,!!!!,!]{{{$////////ut//////////////////////////////////////////////////////(',''',,~!~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~*~~~~~~~~~~~~~~*~*~~~~~~'''''',+@$@+@@+>//// ", +" 0000000jhdjhdddddddddpdddddpdddpdpdpdpddpdppdppdppppppppvpppvvyvvyyvyvovvvoyovooopypoopjvoooooojooojojyojpjpjdjojoojpjjjjojdjjjjjjjjcjjdjjjjajjjjojjjjjjjjcjjcjajaajjjacjajjajacjajjaajaaajjjajaacjacjaajjaaaajaajjaaajaaaaaaaajaaajaa0jaajzzzzzyzj000000b99mq9q9qmqqqqqqqq--m-qqqqq*{~!!,!,!~,~~,~,'~'~'!!,!!~{{{q$//////////////////////////////////////////////////////////////=',''',~~~~~~!~~~~~~~~~~~~~*~~~~~~~~~~~~*~~~~~~*~~*~~*~*~~~~~~~*~*~~,''''''-@+@@@$@+^/// ", +" 0000000dddddjdddddpddddjddddpdpddpppdpdpdodpppdpdppdppppppvvyvyvovovyyyoovvyoypyoyoooooooopjpopjpoopjpjpjojojoojpjojoojdjjjojdjdjojojjcjjojjocjjjjjjjjcjjjjjjjajajjjjjjjjcjcjjjjjcjjaaajaaaajajajjajaaaacjjaaaaaaaajaajaajajaaajaaajjjajazzyzyzzz0000000&9q99qmqq9mqqqqqqqqq-m-qqqqq~~!,~,~,~''~'~,~,~!!!,!,,,!{{{rm>///////////////////////////////////////////////////////////+','',,!~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~*~~~~~~*~~~~~~~~~~~~*~*~*~~~~~,''''''!+@@+@@@@>/// ", +" 0000000pddddhdddddddpddddpdjddpdppddpdppddpdpppppppodpppppvvvyvyvyyvvopypoypopooopoovvooooooyjyjyjojoojooojpjojojjojdjjjjojdjjjjjdaojojjjjjcojjjjcjjjjjjjcjjjcjjjajacjaajjjaaajajaaajjajajaaaajaaaaajajjaacjaaaajaaaaaaaaaaaajaajaj0jaaajzzzzyzzj000000b899q9q99qqq9qqqqqm-mqmqq=q=]!,~,,~'~,~,~,!,~',,~'~'~!,!!{{{rm////////////////((/////////wt(/////////////////////////////,'','',~~~!~~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~~*~~*~~*~~*~~~~~~~*~*~,'''','#+@$@@+@@// ", +" 0000000dddpdphjdpddddpddpddpdpdoddpppdppdodddodpdpdvdppppvyvyovypoyvyoyooyoyooyoovoooooooopjpopjopjpjpjdjojpjojdjojjoocojjjjjjjjjjjjjjdjojjjdjjjocjjjjjjocajjjjcajjjjjaajajjaaaajajcaajcajajaaajaajcaaaaaaaajaaaajaaajaajaaaaaaaajajaaazyzjzzzz0000000&9mq9q9qqqqqqqqqqqmq---qqqq*]~'~'~,,~'~'~'~'~~'!,!!,!'!,,,{{{^////////////////wwt///////twww(///////////////////////////%',,'''!~~~~~~]~~~~~~~~~~~~~~~~~~~~*~~~~~*~~~~~~~~~~~~~~~~~*~~*~~~~~,''''''*+@>)$@+@> ", +" 0000000jhpdddddddpddpddddppddpddppddpdppddoppdpppvppppppvvoyvyoyvypoypovypopypopoovooypojoooojyojoojojjojpjjpjojjdjjooojojdjjdjjjdjjjjajjjjajadajjjcjjcjjjjjcjjjjajaajjjcjjcjjajcjjajajaaajcjacjaaaaaajaaajaaaaaaajaaaaaaaajaajaaaaaj0zzzzzyzzj000000b399q9qqmq9qqqmqqqm-mqmqqq{==!,~,~'~,~'~,~'~'~'~'~'~'~,!,!,~{8////////////////swwws//////wwwx///////////////////////////(,,'',',~~~~~~!~~!~~~~~~~~~~~~~*~~~~~~~~~~~~~~*~~~*~~~*~~~*~~~~~*~*~~'''''',.++@@>+@> ", +" 0000000dddddpddddddddppdddpdppdpppdoddppddppppppppppppppvvyvvypyoyovypjyoyjooooooopoooopoooopjpoopjpjpoojoojojojjoojjjdjjjjjjdjjjjjjjjocjjjjajjjjjoajjjjajjjajjcjjajcaajaajajaajaajcaajaajaajaaaaajaaaaaaaaaaajaaajaajaaaaaaaj0jaaaaazyzzyzyz0000000#qq99mq99qmqqqqqqqmq-m-=q=q]~~'~'~,~'~,~'~'~,,~'~'~'!'!'!'!'*/////////////////swwx///////(tw(///////////////////////////=,'',',~~~~~!~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~*~~~*~~~*~~~*~~~~*~*~~~~~,''''''-@@$@+@$+( ", +" 0000000pdddddpddjdddddppdpdpdppddpddpppppppdpodppppppppovyvyvyyvvoyooyvoppypypoooyjopjooopjojojojojpjojpjjdjpjojojdjjjjjdjjjjjjjjdjjdajojdajjjjcjjadajajjcjjjajjacjjjjajajaajajajaajajaaaaaaaaajaaaaaacjaaaajaaaaaaaaaaajaajaajaaajazzjzzzzzj000000b39qq99qqq9qqmqqqqq-m-qmqqq=]]~'~'~'~'~'~'~'!'~'~'~,'~'~'~'~,.////u/////////////(xu/////////(///////////////////////////@,','''~!~~~~~~~~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~~,,'''''!+@@@@@@@> ", +" 0000000ddpddpddddpdpddpdpppdpdpdodpddodpdodppppdpoppppvyvyovovoypvvvyoyyoojoooopoyooypjooyoooopjooojpjjojojjdjdjjjpjojjjjdjjjdjajjjjjjjajjjjjjocjajajjajjajajajjjaajcajaajajcaacaaaajcaajaajaaaajcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaayzzyzzzz0000000&q999qq9qqqqqqqmqqqmq-m-qq*=],,~'~,~'~'~'~,~'~'~,'~,!,~'~',,=////uwws///////////////////////////////////////////////////!,'',',~!~~~~~~~~~~!~~~~~~~~~~~!*~~~~~~~*~~~*~~~*~~~*~~~~*~~*~*~~~*~,''''',%@++@++++/ ", +" 0000000jddpddppdddpdpddpdpdppdoddpppddppppppppppdpppppvyvvyvyvooyooypoopovovovoyjvojoooojpjpjyjpjoojojpjoojojjjjojjjpjjdajjdajjjjcojjjjjjcjcjjjjjjjcjjajjajcjajcjjajjaajjcajajajjcjaajacjaaaacjaaaajaaaaaaaaaaaaaaaaaaaaaaaaaaaaajzzzzzyzzj000000b3mm99qm99qqmqqqqqq--qmqqq==*~!~'~'~'~'~'~',~',,,'~'',,'~'~'!>////xwww//////////////////////////////////////////////////#','','!~~~!~!~~~~!~~~~~~~~~~~~~~~~~*~~~~~~~~~~~~~~~~~~*~~~~~~~~~*~~,''''''*@+@$@@@@> ", +" 00000000ddpdddpdpdpdppdppdppdddppppppppdppppdpppoppppvyvoyvovyvyovvyopoyvooooooyooopoopooyjpjojojpjpojojpjojojpjjpjjjjjjojjjjdjojjadjcjjjojjjjjcjajjjcjajajjjajajajajcjaajaaaaaaaaaajaaajcajaaaaaaaajaaaaaaaaaaaaaaaaaaaaaaaaaaa0jzzyzzzzz0000000)999qqq9qq9qqqqqqqqq-m--qqq=]~'~'~'~'~,,,'~',~'~'~'~,~',,',,&/////xwws/////////////////////////////////////////////////(,'','',~!~~~~~~~~~~~!~~~~~~~~~~~~~~~~~~~~~~~~~*~~~*~~~~~~~*~~~*~~~~~'''''',#+@@+@$@@ ", +" 0000000addpddddpdppddpddppppdpdpddodppppppppppdppppppovvyvyvovvoyyoyooopovovooopjpjyjyjojoopjpjojojjpjjojdjojjojjocojojcojjjajjjjjjjjjjjjjjjajjjjjajjjcjjaajcjacjaajaacjaajcjaaaacaaaaaaaaaaaaajaaaaacaaaaaaaaaaaaaaaaaaaaaaaaajzzyzzyzz0000000b999999qqmqqqqqqqqqmm-qmqq=]*]!!,~',,,'~'~,,,,'~',',',,'~'~',.//////ux////xt(///////////////////////////////////////////-,'',',~~~~!~~!~~~~!*~~!~~~~~~~~~~~~~~~~~~~~~~~~~*~~~*~~~*~~~*~~*~*~,''''''-@+@+@@@+^ ", +" 0000000adpdpddppddppdppddodpdjdpdppdpppppppppvpppppvyyyoyyovyovoopoyoyooovjpoyoooyjpyjypojoojpjpjpjojpjjojjdjjdjojdjjoocjjojadjjjjjjjcjjcjjjjajcjajajjcjjaajjjjajajajajcjaaajajajcaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaazzyzzzzzz00000008999qmq99q9qqqqmqqq-mq--qqq=*]'~''~'~'~'~'~'~'~'!,~'~'!,,,'!,,-(/////////uwwwu/////////////////////////////////////////>',''',,!~~~~~~~~~~~~!~~~~~~~~~~~~~~~~~*~~*~~~~~~~~~~~~*~~~~~~~~~~~~,'''''',+@$@+@++> ", +" 00000000ddddpdpdpddpdjddddpdpdppppdoppppdppppppppppvvvyvvyyovoyyopypopooyooyjpooopjyjpjjopjooojojojpjjojdjojjjjjjjjjjjojojjjjjjcoadaojjjjjcjjcojjjjcjjjacjaaaaajacjaaaaaajacjaaaajajaaaaaaacjaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0jajzzzzyzyz0000000b99q999qm9qq9qqqqqqm-q-qm{qq]]~'~,~'~'~',,',,'~',,'~',,','~',,,,,%////////xwww//////////////////////////////////////////!','','~~~!~~~~!~~~~~~!~~~~~~~~~~~~~~~~~~~~~~*~~*~~~*~~~~~~~*~~~*~*~,''''',%+@@+@+@+/ ", +" 0000000adpdpddpdodpdpdopdppdpdpppdppppppoppppoppppyvyvyovyvopypyoooooopoopyoojyoopjyjpoojpjojpjpjojpjojjjjdjojdjoadjjjjjjocjjjjjajjjjcjjjoajjjcaajjajajjjajjaajajcajaajaaaaaaaaaaaaajcajaaaaaaaaaaaaaaaaaaaaaaaaaj0aa0aaaaaazzzzzzzza00000008999qq9q9qqqmqqqqqqmmmmm-qq*=*'~''~'~''~'~'~,,'~'~''~'~'~',,,',,',!$//////(xwu///((////////////////////////////////////#'',','~!~~!~~!~~~~~~!*~!~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~~~~*~~~*~~~~~,''''''~@+@$@@$@> ", +" 00000000dpdpdpdpddpppdddoddodpppdopppdppdpppddppppovoovovyyooyooyoypyoyoooopjypjooopjyjoojpjojojojojojpjojjjojjjjjjjdjjjcojjjjjjjcjjjjjcjajjajjjjjjajaajcjacjjaaajacjcjajaaacjaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajzzzzzzzz0000000$9999999q9q9qqqqmqq-q--m=qqq*=~,,!,',,~'~','''~',,'~'','','~'!,,,'''~q@//////(////xwx///////////////////////////////////,,'''',~~~~~!~!~~!~~~!~~~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~~~~~~~~~~,''''''#+@+@+@+@/ ", +" 00000000pddpdpdppdpppdpdpdpppdpppppppppoppvpoppppyvyvyvoovypypypoooooopyjoypjpjoooojpjooojooojpjojdjojojjdjjjdjjdjjjcojjjoadjjjjjjjajjjjjajjjajajaajcajajajacaajaajaaajcaajaaajcaaaaaaaaaacaaaaaacacaaaaaaaaaaaaaaaaaaaaajzzyzjzzz000000ab99q9q9mq9q9qmqqqqqq-mqmmqqqqq*'!'~'~',','~,~'~',,',',~'~,,,',',',,,'{{rm>////////swwwt/////////////////////////////////-''',',~~!~~!~~~!~~~~~~!~~~~~~~~~~~~~~~*~~*~~~~~~~~~*~~~*~~*~~~~*~*~'''''''-@+@+@+@+( ", +" 0000000dpppddpdppdppdppdppdopppppdoppppppvdpppovyvyyoyvyooyjyooypyooooyoojyoopoyjpjypjyjpjpjjojdjjjojoojjjdjjjjjjjojjjjjjajjjcojcjjcjajcjajajcjajcojajaajajajaajaaaaaaajaaaaaaajaaaaaacaaaaaaaaaaaaaaaaaaaaaa0aaaaaaaaaazzzzzzyzj000000089999m99q9q9q9qqqqqqm-m--qqq={],!'~'~'~'~'',',',~',~',','~','~',,',,'{{{r{8///////swww/////////////////////////////////>,,'',,,~!~~~~~~~~~~~~~~~~!~~~~~~~~~~~~~~~~~~~*~~~~~~~~~~~~~~~*~~~~~!,''''',.+@+@$@@@ ", +" 00000000pddppdpdpdpdpppppppdpppppdppppppppvppvpvvyopyvovypypoyopojpypoooypooyjpjyojojpjpjojpjojojdjdjjjojjjjdjjdjjjjjdjadjjjjjjjjjjjjajjjjcjjjjajjaaajcjaaajcajcajcaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaabaaaaaaaaaaa0aaaa0azzyzyzzzz0000000)999999q99mqmqqmqqqq-mq-qm{qqqq'',,,',,','~'~'~,,',''~,,,'',,',,'!',,'~{{{{{{$///////tu/////////////////////////////////~,'',''~~~~!~~!~~!~~~~~~~*~!~~~~~~~~~~~!~~~~~~~~~~~~~*~~~~~*~~~~~~~~,''''',&+@$+@+@+/ ", +" 0000000appddpdpppppppppdppppppppppppppppppopppvvyyvoyovooooopoyopojyooojyjojyojpjyjpjojoojojpjjojjjojdjjojjjajjjjjjjajjajjcojjjjjjjjjjjajjaajacjajjcjjajcjaaaaajaajacjaaaaaaacaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajzzzzzyzza000000b8999999qm9q99qqqqqqqmmm-qq=qq*{!,',,,,,,~',','',,~','','~,,',',,,,',',{{{r{r{{r$///////////////////////////////////////+',',',~~!~~~!~!~~!~!~~~~!~~~!~~~~~~~~~~~~~~~~~~~*~~~~~~*~~~~~*~~*~~,'''''']+@+@)@$@> ", +" 0000000jjpdppdpdpddppppppppppppppppppppppppvpvovoyyvyovypyyoooyopoyopypoopjpjyjpjojpojojpjojoojooojjjdjjjocojjjdadjjocjjjjcjjcjcajcjcjjajjcajjjaaajjaaaaajajcaaaaajaacajcaaaaaaaaaaacaaaaaaaabaaaaaaaaaaaaaaaaaaaaajzzzyzzzzj0000000)999m9q99999qq9qqmqq-m-q-mqqqqq{,'~'~','',,,,,,~''',,,~'''','~','',',''{{{{{{{r{{9>/////////////////////////////////////,',''',!~!~~!~~~!~~!~~~~~~~!~~~~~~~~~~~~~~~~~~~~~~*~~~~~~*~~~~~~~~~~,''''''#+@$@@@+@/ ", +" 00000000ddpdppdjdpppppdpppppppppppppppopppvpvyvyvvovovooyopypoooyjoojyjpjyoojpjyjpjojpjojjpjojjjjjdjjjjojojjdajjjjjjjojjjjjjjjjjjjjjjajjcjjaaajcjaaaajaaajaajaajaaaaaaaaaaaaaaaacaaaaaaaabaaaaaaaabaabaaa0aa0aaaaaazzzzzzzzz0000000)39999999qqq9qm9qqqqmmmmmmqqqqq{r!',',,'{','~''',~'~'',',~,',,'',,',',,,{{{{{{r{r{rrm///////////////////////////////////%'','''~~~~!~~~~~!~~~!~!~~~~~~!~~~~~~~~~~~~~~*~~~~~~~~~*~~~~*~~~*~~~,''''''=++@@+$@+> ", +" 00000000dppdppdppdpppppppppdoppppoppppppvpppvovyyvyoyovoyoooooopyoyoooyjoooyjpjyjoojojpjojojpjdojjojdjjjjjjjjjjjcjjjajjjjjjjjjjjajajajajjcjjaajjajajcjcaacaaaacaaaaaaaaaaaaaaaaaaaaabaaaaaaaaabaaaaaaaaabaaaaaaaazzyzzzyzz0000000089999999m999q9qq9qqqq-m-m=qq=qqr{{'~',,,,',',,,,'',',',,',','',',',',''~r{r{r{{r{rr{r^/////////////////////////////////>',''',,!~!~~~~!~~~~!~~~~~~~!~*!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~~~'''''',+@+++@++> ", +" 0000000apdpdppdpppppppppppppppppdpppopvpvvvyyovyovvyovooyoypyooopopyjpopojpjyjpjojpjojpjdjjjojjojjjjjdjjdjjjojojjjjdajcjjcjcjajjcjajjajjajcjaaacaajjaajajaaaajaacaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaa0aaaaabaaba0jzzzzzzzz000000002999999q99q99q9qmqqmmmmmmmqqqqq{{r{',,'',,,',,,',',,,,',',',',,'',,'',',{{{{{{r{r{{r{$(/////////////////////////////////='',,',~~~~!~~~!~~~~~!~~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~~~~~~*~~~~~~~*!,''''''-++@@+@++^ ", +" 00000000jdppdppppppppppppppppppppopppvpppvpvyvoyvoovoyoypooojpyjyjpjyooooyjpjyjoojpjojojojojojjjpjojjjjjocjjjjadjjajjjjjjjjjcjajjjcjajaajajajjajaaaajcaaajaaaaaaaaaaacaacaacaaabaaaaabaaaaaaaaaaaabaaaaaa0aaaajzzzzzzzzj0000000)89899999999mq9qq9qqqm-q-m=qqqqq{r{{{'~'!',,,,'''~','',,'',,','',','','''{{{r{{{{{{r{r//////////////////////////////////+,,'''',!~~~!~~~~!~!~~~~~!~~~~!~~!~~~~~~~~~~~~~~~~~~~*~~~~~~~~~~*~~~!''''''!@$++$+@+> ", +" 00000000pdpjdppdoppppdopdodopppppvpvpopppovoyyovyyoovyooypyoyjypjyojoojojyjyjpjojoojojojpjojdjjjjojjdajjojjcojajjcjjjjcjjjjjjjajjjajcjjaaajaacjcjaaaajacaacjaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaabaaaaaa0aaaaaaaaazzzyzjzzz0000000b399999999m999q99qq9qmmmm-qqqqqqq{rr{{',','',',,,',',,'',,',,'',,'',',','{{{{{r{r{r{r{$//////////////////////////////////,','',,~~~,~!~!~~!~!~!~!~~~~~~~~!~~!*~~~~~~~~~~~~~~~~~~~*~~~*~*~~~~~,'''','#+@@+@++@/ ", +" 00000000ddppdpppppdppppppppppvpppppppvppvoyvvyovoypoooypjoopjyyoopjypoopopjoojpjpjpjoojjojjjpjjdjjjjojjjjojjjjjojocjjjjjajajcajajajajcjajcjajajacjaaajaaaaaaaacaacaaaaaaaaaaaaabaaaabaaaaaaaba0aabaabaaba0azzzyzzzyz00000000893i89999999q99qm9qqq-m-mmqq=qqq{r{{r{~'~'~,,',''~''',,','''',,'','',',''{r{{r{{{r{{rm//////////////////////////////////%''',,'!~~~~~~~~~~~~~~~!~~~~~~~!~~~~~!~~~~~~~~~~~~*~~~~~~~*~~~~~~*~~,''''''*++++@$@+> ", +" 0000000apppppppppopppppppppppopvppvppvvvvyyvovyoyyopoooypyooopooyjojyjojoopjojjojojdjojdjojjjjjjdjjjojjjjjjjdajjjjjjjajjjjjjjajcjajajaajajaaaajaajaaaaaaaaaaaaaaaaaacabaacaaaaaaaaaaaaaaaaa0aabaaaaaaaaaazyzzzzzzz000000002399999999999qm99q9qmmmmmm-qqqqq{rrr{r{r''','',,',',,,'',',','''',','''',{{{{{{r{{r{rr>/////////////////////////////////(,,'''',~~!~!!~!~!~!~!~~~!~!~~~~~~~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'''''',.+@+++@+> ", +" 00000000jppppppdpppppppppppvpdpoppoppppvovoyovoypoyoypoyojyoyjpjopypjoyjojojpoojpjjjojjojjjojojjjcjjadjadjjajjajjjjcjjjcjjajajjjcjjajajcaajaaaaaacaaajcaacaaaaaaaaaaaaaaaaabaabaabaaaaabaaaaaaaabaaaaaaazzzzyzzyzz0000000)9999398999999999q9qqmmq-mmqqqqqqrr{{rr{{{,,','',,',''',,',','',','''',''{{r{r{{r{r{r{8//////////////////////////////////=,',',,!~~~~~~!~~~~~~~~~~~!~~~~~*!~~~~~~~~~~~~~~~~~~~*~~~~~~~~*~~~~~'''''',-+$+@@+++( ", +" 00000000jppppppppppppppppoppvpppppvvvvvyyvvoyooyyoooyjpypojpyjyjojyyjpojyjojojjjoojpjojoojojdjoojojjjjjjadajjcjjcojjcjjjacjjajajaacjaajacjajcjaaajaaaaaaaaaaacaaaabaaaaabaaaaaaaaabaa0aaaabaa0aaaabaaajzozzzzzzj0000000b33939i99999m99qm9q99mm-mmmqqqqqq{rrr{{rr{{{',',','',','''','',''',','''',{{qm{r{{{r{rq(/////////////////////////////////@',''''!~~,~~,~~!~~~!~!~!~~~!~!~!~~~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~!''''''!++@++$@+> ", +" 00000000jpdpppppppdopppppoppvpvppppppvovyovyypoopypyoyjoyjyopoypjojyjpjpjpjpopjoojjojjjdjjjjjjjjjjocjjjjjjjjjjjjajjjajjjocjajajajjajaaajcaaaajaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaa0aazzzzzzzzzz000000008789989899999999999qqmmm-m-qqqqqqrr{rr{r{r{{{'',',',','',''','',''''''''{{{r>/$qrr{{r{$//////////////////////////////////!,'',',!~~~!~~~~,~~~~!~!~~!~~~~~~~~~~!~~~~~~~~~~~~~~~~~~~~~*~~~~~~~~,''''',%++++@+++/ ", +" 00000000pppppppppppppopppvppppvvvvvvyyoyoyoyyoyjyjpjpyypoojojyjypjojyjojojjjojdjjojoojjjojjjdjjdajojjjjjcjjjjjjjcjjjaaajaajcjaaajaajajajajacaaacaaacaaaacaaaaaaaaabaaaaabaabaaa0baaaaabaa0aab0aaabjzyzyzzyzj00000000289397999i899999q9q9qmmmmmmqqqqqqr{rrrr{r{rr{{{'','''',',&-'','',''',','~{{{m////${r{rq//////////////////////////////////#'','',!~!~!~~!~~~!!~~~~~~~~!~~~~~~~~!*~!~~~~~~~~~~~~~~~*~~~~~~~*~~~,''''''=+@+@++@$> ", +" 000000000oppdodopppppppvppvpopppppppvvyvvypoypypyyoyjojyoyopyjpjoopjojpjoooojjjpjoojjjdjjdjjjjjjojjcjjjjojjjcjjjjjajjajjjjajjcjajcjcaaaaaajaaaaaaaaaaaaaaaaaaabaaaabaaaaaaaaaaaaaab0aa0aabaaabaaazzzzzzzzzz00000000)39398i8989999m999m9mmmmmmmqmqqqq{rrr{r{rr{r{r{{{,''','''!//@*''''',''''{{{{r///////8{r>/////////////////////////////////(,',''',~!~~~!~~!~~~~~~~~~!~~~!~~~~~~~~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'''''',#@++$@++@ ", +" 000000000ppppppppoppvpppppppvovvpypoyooyoyooyjyopypoyopjpjyjoyjyjooojopjojojpjjjpjoojjjjjjjjjjjjjojojjjcjjjjajajajjcjjacjajjjaaajajaacjaaaaajaacaaaaaaaaabaaaaaaaaaaaa0aa0baaaaaaaaaaabaaaaaaaaazzzzzzjzz00000000b33979393i999999999999mmm-mmqqqmqqrrrrrrr{r{r{rr{r{~'''','.////.~''''',~{{{r{$////////($//////////////////////////////////-'',','~!~!~~~~!~!~!~~!!~~!~~~!~~!~~~~~~~!~~~~~~~~~~~~~~~~~~~~*~~~*~,''''''-+++@)@$@( ", +" 000000000ppppppppppppypopvppppopvppyyvoyypoypyoyjopjyoyopjpoojpojopjojojpjojoojjjjjjdjojjdjdjjjjjjcjjjjjjjjjjjjjajjajajjaaaajaaajcajaaacaaacaaaaaacaaaaaaaaaaaaaaabaabaaaaaababaaaaaaa0aaa0aajzzzzyzzzz00000000b>@8398i898i899999999qmmmmmmmqqqqqrrrrrrrrrrr{r{{r{r{{{,''=///////%,',,{{r{{{q////////////////////////////////////////////@','',',~!~!~,~~!~~~!~~~~~!~!~!~~!~~~~~~!~~~~~~~~~~~~~~~~~~*~~~~~~~~,'''''',+@$@$@+@@ ", +" 00000000appppppvpvpppppvpvvvpvvvvypoyooyyoyjyjpyyoyjoojyoooyjooojooooojjoojjpjooojjjjdjjjajjdajjojjjjjjcjjcjcjjjajjcjaajcjajcjaajaaacjajaaaaaaaaaaaabaacabaabaaaaaaaaaabaa0aaa0babaaaababaajzzozzzyzz00000000b(>>>$839i8i8999999999mmmmm-mqmqqqrirrrr{r{rr{r{rr{r{{{{{{~>////////(m{r{{{{r{>////////////////////////////////////////////!,'''',~~~!~!~~~~!~!~!~~~~~~~~~~~~~~~~~~~!~~~~~~~~~~~~~~*~~~~~~~~~~~,''''''%+++@+@+@/ ", +" 00000000apppopppoppvppvppvpvvpvpyovyypoopyypyjyjpoyoyjyooojoooopjojoojpjjojjjjjojojjjjjojjjjjjjjjjjjjjjjjjjjajcajajjajjajaaaajaaajjaaaaaaaaacaaaaaaaaaaaaaaabaaaaaabaaaabaaaaaaa0abaaaa0ajzzzzyzzzz000000000@>@>@>@$38i8i898999q9mmmmmmmqq9qqmrrrrrrrrrrrrr{rr{rrr{r{{8///////////>9{{r{{8////(s//////////////////////////////////////#'',,,',~~!~!~~,~~~!~~~~~!~~~!~~!~~~~!~~!*~~!~~~~~~~~~~~~~~~~~~~*~~~,''''''*++++++++> ", +" 00000000jppppopppyppyppvvvvpoppyyoyoyyooyjyopyjyjpyjpjyjypjyjjyjpjojojopjooojdjjjdjjdajjjjjdajjcjjjjjjjjjajjjjjjajcjajajajaacjaaaaaaaaacjaaaaaaaaaaaaaaaaaaaababaaaaabaaaba0aa0aaaaabaajzzzzzzyzza00000000&*#@>>>@>@198i8i999999mmmmmmm9qqmqrirrrrrrrr{rrr{r{r{r{r{{r//////////////$q{r{(////xwwu////////////////////////////////////,,''',,~~,~~!~~~,~~!~!~,~!~!~!~~!~!~~~~~~~!~*~~~~~~~~~~~~~~~~~~~~~~~'''''''#++@+$@+@ ", +" 00000000jvpppvpppppvpvpvpvppvvvvoyypoyoypyooopypjoyjpjyjpjopjojojpjpjjjpjjjjjojjjjjjojocjjjojjjjjjcjajcjjajajjcajajaajcjcjaajcaaajcaaaaaaaaaabacaabaaabaaaaaaa0aab0aaa0aabaabaaabaa0azzzzyzzzzzj0000000bl&***#@>>@>>@1i9989999mm%mmmm9mq999rririrrrrrrrr{r{r{r{r{r{$///////////////(${$////swwwt///////////////////////////////////-''''''~~~!~!~,~~~~!~~~~~~~~~~~~~~!~~~~~~~~~~!~~~~~~~~~~~~~~~*~~~~~~,''''''-@$++@+++( ", +" 00000000avpvpvppypppyppopyvvvyoyoyoypjyyjpyjyjypyooyjyjyojoopjojojojpjjojpjojjojojjjjojjjjjjdajjjjjjjjjajjcjjjjajajajajaajaaajaaajacaaaacaaaaaaaaaaaaaaaaaaaaaaaaaa0aaa0aaaaba0aabazozozzzzzz000000000)q**&*&-$@>@>@>$899999mmmmmmmq99qqqiirrrrirrrr{rrrrrr{rr{rrm////t/////////////(/////(xww///////////////////////////////////>,',,''!~,~~~!~~!~!~!~,~~~~!~~~!~~~~!~~~~!~~!~~~!~~~~~~~~~~~~~~~~~~~!'''''',.++@++++@ ", +" 00000000avpoppppvppvpypvpvppvypyoyyopyoyjpoyojyjoooopjyjpjyjoyjojojojojojjjjoadadjjjjjocjajjjjjjjjjjjcajjjjajcjjcjaaaajaajcaaaaaajaaaaaaaaaaaaaaaaaaaaba0abaabaaabaabaaba0aabaaaazzzzzzzyzz00000000be**-*****=%$>>>>>>$8898mmmmmmm9qq9qirrriirrrrrrrrrr{rr{r{r{r>///twwt///////////////////uu///uwt/////////////////////////////~','',,~~~,~~!!~!~~~~~~~~!~~!~!~!~!~~~!~~~~~~!~~~~~~~~~~~~~~~~~~*~~~'''''''&+++++@$+/ ", +" 0000000000vpvpypvpoppppypvypoyypyoyooyopyooooyjpooojoojoojojpjojojjojpjjoojdjjjjjjjjjjjojjjcjjcjajcjjjjcjjaajjajaajajcaacjajaaaaacaaaaaaaaabaaababaaaaaabaaaaaaa0aaaaa0aaba0aabazzyzzzzzzz000000000)**-**&*-*===%@@>@>@>1mm8mmmm99999qiiiirrrirrrrrrrrrrrrrr{r{8///(wwwx////////////////////////xwwx///////////////////////////.,'',''!~,~~~~~~!~~,~!~!!~!~~!~~~~~~!~~~~~~~~~~!*~~~~~~~~~~~~~~~~~~~,'''''']++@$+++@> ", +" 0000000000oppppvvpypyvpvppvpyoyooyyyjpjyoyoooyooyjyoooyjpoojojpjpjojjojjjjjjjjjdjjdjjjjjjjjjjjjjjjjjajjjcjjaaaaajcjaajjaaaajcaaaaaaaaaaaaaaaaaaaaa0abaaaaabaabaaba0abaaaaaa0aazzzzzzzzzz000000000)****-**=**-***m#@>>@>@$#mmmmm9q9mq9rirriirrrirrrrrrrr{r{rrrr(///(xww(///////////////////////uwwws///////////////////////////,',''''~~!~~,~,~!!~~!~~~~~~~~~~~~!~!~~~!~~~~~~~~!~~~~~~~~~~~~~~~~~~~,''''',#++++@+++/ ", +" 0000000000pvpyppppvpvvvvypyyoypyjpyyoypjypjyjooooojpjoojyjoojojjojojojpjojoojjjjjajjjjdajjjjjjajjjjajajjaajjjjaaaajaaaaaaaaaaaaaaaaaacaaaaaaaaaabaaaaaba0aa0aaaaabaaabaabaajzzozyzzzjz000000000)!]]***=-*-**-*===m#>>@+.+.8m8m99999iiiiririrrrrrrrrrrrrrrr{rrq$////(s///(wx(//////////////////(xw///////////////////////////-,'',,,!~~~,~~~~!~~~~~!~,~~,~~~!~~~~~!~~!~~~~~~!~~~~~~~~~~~~~~~*~~~~,''''''=+++++$++^ ", +" 000000000jppypypyppypppppyoyoyyjoyjypyjypyoopjypjyjyjpjojoojoojpjojjjjjjjjjjjjjjjdajjjjjjjjjjjajcjajaajjcajcjajaacajcaaaaaaaaajbaaaaaaabaabaaaaaaaaaaaaabaab0aaabaaa0abazzzzzzzyyzj00000000a&]]*]=]]****&**=*===*m+++.+++.m999q9iiiiiiririirirrrrrrrrrr{r{rrrm>///////twww(////////////////(/////////////////////////////>,''''',~,~~~~,~!~,~,~!~~~~~~,~~~!~!~~~!~~~!~~~~~~!~~~~~~~~~~~~~~~~~!'''''',.@$@++@+@ ", +" 000000000ovpppypvpvpypyoyopyopyypoojyojyjooyjyjyjpjojyjpjoojojjojojoojojdjojdjjjajjjjjcjjcjjcjjjjjjajajjajajacjajaajaaaaaacaaaaaabaaaaaaaaaaabababaaaa0aa0aaba0aababajzozyzzzzzzz000000000&**=]=]*=*=*=*=&*=*====-&...++>>$m99iiiriiiiirrrirrrrrrrrrrrrrrrrrrrm//////wwwx///////////////////////////////////////////////],,''',~!!~!~!~~~~!~~~~!~,!~~~~~~~~!~~~~!~~~~~~~~!~~!~~~~~~~~~~~~~*~,''''''&++++++++/ ", +" 000000000jpvpppypypypppoyyyoyjyoyoyoyjyooooooojyjopjojyjoojpjojjojdjjjjjjjjjjjjjjjjjjjjjjjjjjjaajcjjajaaajajaaaaaaaajcaaaaaaaaaajbaaaaaaaaaaaaaaabaabaabaaaaabaa0a0jzzzzzzzzzz000000000b;=*]]*]=]]]*=****=-*-**-=-&%.+@@@>@$9iiiiiiiriiriririrrrrrrrrrrr{rrrrrr$/////tw(//////////////////////////////////////////////+,'',,,,~~,~~~~,~!~~,~!~~~~~~~!!!~!~~~~!~~~~~!~~~~~~~~~~~~~~~~~~~~~~,''''''!+++@$+++> ", +" 0000000000ypypppppypyyooyopyopoyjpyjpooooyjooyjpojyjojjojjjpjojjjjjdjjjjjjjdjjdajjjjjjajjajjajjjjacjajaajaaacjaacjaajaaaaaaaaaaaaaabaababaaaa0aa0aaaaaaab0a0aabaazzyzzzzzzyz000000000b-!]]=!=]*=*]]]=]=****=*&=-*--&#>>>@>>^8iiiiiiiiririrrrirrrrrrrrrrrr{r{rrrr$////////////////////////////////////////////////////,'','',!!~!~,~~~~~,~~~!~!~~!~~~~~~~~!~~!~!~~~~~~~~!~~!~~~~~~~~~~~~~~'''''''#++++)@$@/ ", +" 0000000000opyvypypyppypyoyoyoyoyjyoyoyjpjyoojyjojpjypjooojjjojoojjjojdjjjajjjjjjcjjjjajjjcjjjajajjjcjajcajjaajjaaaaaaacaaaacaaaaaaaaaaa0aaabaaaaabaab0aaaabaaaazzzzzjzyzzz000000000a=]]]]]=!=]]*]=**=*===*-*=&=&*=**-$>@>///^8iiiiiiiririirirrrrrrrrrrrrrrr{r{rr/(/////////////////////////////////////////////////%',,'''~!~!~~~~,~!~~~,~!~~~~~!~~~,~~!~!~~~~!~!~~~~*!~~~~~~~~~~~~~~~~,''''''*+++@$@++> ", +" 0000000000jpppppypvyooyyoooypjyoooojpyjyjoooooyjojjjyjjojojdjjojojjjjjjdjjjjjjojjjjjjcjjjajcjjaajjajaajaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaabaaaabaa0aa0aabaa0aabjzyzzzzzzyz0000000000b*]*!]]!]*=]*=]]]*]*]****--=&=--**=*&@//////$9iiriiirirrrririrrirrrrrrrrrrrrr$//////////////////////////////////////////////////(,''',',!~!~,!~!~~~!~~!~!~,~,~~~,~!~~~~~~!~~~~~~!~!~~~!~~~~~~~~~~~~~!'''''',#+++++++@ ", +" 00000000000pyvvpyppyoooypyoyoooooyjyjypjyjyjojooopjojpjoojjjojjjojjojjjjjadjajjjcjjjjjjajjjcjajaajajajcjajcaaaaaaaaaaaaabaaaaaaabaabaaab0aaaabaabaaaababaazzzzzzyzzzz0000000000b&=]]]]]]]!!=!]=]=]=]*]==-*=-=&=*=*==**%///////$iiiiiriiirirrirrrrrrrrrrrrrrrq////////////////twu////////////////////////////////*,'''',~!~!~~,~~~,~~,~~!~~~~~~!~~~~!~~!~!~~~!~!~~~~~~*~!~~~~~~~~~~~~,''''''&++$+++++( ", +" 00000000000ypvvvyvypyoyjyjyoyoypjyoojyjoojypjojjoojyjjjojojjojjjjjjjjjjjjjjjjjjjjjjjcjjajjjjajaajaaaajaaajajcaaaacaaaaajaaaaaaaaaaaaaaaaaab0aaaaaba0aaajzyzzzyzzzzj0000000000).#%-]]]]]]]]]]**]=~=]=]*=-=-=*=*=*=*=~~~].//////^8iiiiiriririrrirrrrrrrrrrrrr$///////////////(wwwt//////////////////////////////@',,,''~,~!~!~!~,~~~!~!~~,~!~~~!~~~~~!~~~!~!~~~~~~~~~!~~~!~~~~~~~~~~,''''''!++++@+$@> ", +" 0000000000jvypvpyjyoypypyjpyjooooyooyooojypjyjojojojojojojjojojdjjjjocjjjjjjjjajjjjjjjjaajcjajcjajaajaaajaaajaaaacaaaaaabaabaaaaaaaabaaaaa0abaaaaaaazyzzzzyzzzzj000000000b)#####%=!]]]]]]]!]*]=]=]=-=*-=-===*-*=~~!!~~=@//////^8iiiriiririrririrrrrrrrrr8////////////////twww(//////////////////////////////,,'''',~~~,~!!~!~,~~!~!~!~!~,~~~,~~~!~~~~~~~~~!~!~~~~~!~~~~~~~~~~~~~,''''',%++++++++/ ", +" 00000000000pypyvyyooyjyoyoooyjpyjojyjpjyjjojojojoojojjojojjjjjjjjdjjjojjjjjjjjjcjjajajjjjajajjaajaacjaaaaaaaaaaaaaaaaaaaaaaaababaaa0aabaabaa0babazzzzzzzzzzzj0000000000b+########%=]!=!]]]]]]*]*===-=-*=***=*=*~!~~!~~!->//////$9iiiiririrrirriirrirrrr(/////////////////uxt//////////////////////////////#''',''~,~!~!~~~,~~~~~~,~!~~~!~~~!!!~~~!~~!~!~~~~!~~~~~~~!~~~~~~~~~~,''''''*+++$++++> ", +" 00000000000jpypoyoyoyoooyjpyjojypyjyjojyjpjyjoojpjoojjdjojdjojjjjjjjajjjjjcjjjjjjjajjajajajaajcaajaaaaaaaaaaaaaaabacaaaaaaaaaaaaaba0aaaa0aaa0ajzyzozzyzjzzz0000000000b############-]]]]]]!]]!==-=-=*-===]=**~~~!~,~,~~~~%(//////$iiiiiriiirirrrirrirr$//////////////////////////////////////////////////(,',,'',~~~~~!,!~~~,~,~~~~~!!~~!~~~~~,~!~~!~~~!~!~~~~~~~~*~!~~~~~~~~!'''''',#++++$++@ ", +" 00000000000yyyoyoooyoyooyoyoyjjooooojpjoyjpjjjjjojojjjjjjjjjjojjjjjjcjjjjjajaajjjaajcjjaajaajjaaacjacjaaacaaaaaaaaaabaaaaaa0abaaaba0abaabajzzzzzzzzzzzj00000000000)###############-]]]]]]]]===*-=-*]*]=]=!,,,~~!~~,!!~~~#///////1iriirririirrrirrii(////////////////(/////////////////////////////////=,''','~~,~,!~~!!!~~~~~,~!~~!~~~,~~~~~~~!~~!~~~~~~!~!~~~!~~~!~~~~~~~,''''''-++++++++^ ", +" 000000000000yoyoyooooooooojyyjyjyjyjyjjojjoooojojjojojojjojjjjdjjjojjjjjjjjjjjcjjajjjajcajjaaajjaajaaaaaaaaaaaaaaaaaaab0abaaaaaaabaa0aaazzzyzozzzyzz00000000000b #%##%)########%-!]]]]===-====*=]*]*]!,,,,,~,~!~!,~!!*+////(/^8iiiirirriiirrrr$///////////////////(//////////////////////////////@','',',,~~~~!!~~~!~,~,~~~~~,~!~!~~~~~!~~~~~~~!~!~!~~~~~~~!~~~~~~~~~,'''''',++++++++@ ", +" 000000000000jyooyoyooyoooyjpjyjpjyjyjyjoojojjojojjojjjjdjjjjjjjjjjjjjjcjajjajjjajaajaajjaajaaaaaaaaaaaaaaacaaabaabaaaaaaaaa0aaaaaabaazzozyzzzyzzzj00000000000b %####%###)%####%=]]======-*=]*=*]],,,,!,,!~,~!~!!!!~!->//////>9iiiiirrriiir8///////////////////////////////////////////////////~,'',',~!~,~~~~,!!~~!~~!!!~~!~!~~~,~,~~,~~!~~~~~~~!~~~~~~~~~!~~~~~~~,''''',%+$++++$@/ ", +" 000000000000ooyoooyoyjyjyjyjoyjyjpjyjooojoojojojojojjjjjjjjjjjjjjjjjjjcjjjajjcjaajajajajcajcaaajcaaaaaaaaaaaaaaaaaaaabaabaaba0aaazozyzzzzzzzzz000000000000 %####%#########&-==-===]]]*]]]]!'!'!'!,,,,~!!!~,~,,,!&(//////$9iriiiirrri(//////////////////////////////////////////////////#,'',''~,~~~,~,~~~,~!~!~~~~,~~,~~~!~!~~!~!!~!~!~!~~~!~~!~~~~~~!~~~~~,''''''~+++$++++> ", +" 000000000000jyjyooyjypjyoyjyjojojyjjojojojjpjjjjdjjojjojjjjjjjjjjjjjjjjajjjjjjjajajaajajaajaaaaajacaaaaaaaaaaaabaaaaaaaaa0aaaayzzyzzozzozzj000000000000b #############%%%====*]!]]]=!,,,!'~'~'~,,,,~,~!!~!~,!~#(//////$iiiiiiii1///////////////////////////////////////////////////'','',,~!~,~~~~,~!~,~!~~,~~~~~!!!~~~~~~~~~~~!~~~~~~~!~~~~~~~~!*!~~~~'''''''#++++++++ ", +" 000000000000jvjyopyjyjpjyjyjyjpjyjyjoojojjoojjojjjojjjjjdjjjadajjjjjjjajaaajcajaajaaajaajaajaaaajbaaaaaaabaaaaaaabaaaabaajozzyzzzzzyzzzz000000000000 %#####%###%#%#%&=]]]]]]],','~'~,,',,,!,!,,~,~,~,~,,,*.///////8ririi9(///x(/////////////////////////////////////////////-,','''!~!~!~,~!~!~!~~,~~~~!~,~~~~~~~~~,~~~~~~~~!!~!~~~!~~~~~~~!~~~~'''''''-+++++$++( ", +" 000000000000jyojyoyjyoooojyjoojjpjyjjoojjojjjojjjjojjjjjjjjajjajajajjjjjajjajajajaaaaaajacaaaaajaaaaaaaaaaaabaa0abaaajzzzyzyzozzzyz0000000000000b %##%##)%%#%#%#%%-!]]]],'''',''!~'~'!,,!!,!,~,~,!~!!,!*@//////^8iii$///twwt///////////////////////////////////////////>','',',~!!!~~~,~~!!~!~!~,~,~~!~,!!!~,~~~~~~,~~~~~~~!~~~~~!~~~~~~~!~!'''''',.+++++++@ ", +" 00000000000000poyjyjpjoyjoojoyjojjojojojjojojjojjjjjjjjjjjjjjjjjjjcjaajjjaajajajcjajaaajaaaaaaaaaabaaaabaaa0aaaaaajzzyyzzzzzzzzzj000000000000b %###%#%#%#%####%=]]'''''''~',',~'!',,,,,,,,~~,~,~!~!,&>//////$98///(wwwx///////////////////////////////////////////!,'',',!~~~,~,~~~~~!!~,~~~~~~~~!~~~~!~!~,!!!~~!~!~~~~~!~!~~~~~~!~~~!,''''''&+$++++++/ ", +" 0000000000000jyyojyyjpjooyjjyjoojjojojojjjjojjojjjjjjjjjjjcjjajjjjjacjajjacjajajaaaaaaaaaaaaaaaaaaaaaaaaaaaaajzzzyzzzzozyzyj00000000000000 %%#%#%%########-,''''''''',~'!,~'~'~!~,!,,,~,,,,!!!!!%(//////^////tww(///(//////////////////////////////////////.'','''~~,!~!~!~,~,~!~~!~,~!!!~~~,!!~~~~~~~~~!~!~!~!~~~!~~~~~~~~~~~~,'''''']++++++$+> ", +" 00000000000000ypyjyjyjjjyjjyjoojojojjpjojjjjjjojjjjjjjjjjjjajjajjjajajajjaaaaajcjaaaaaaaaaaaaaabaaaaabaajjzozyzzzozyzzzzj0000000000000b %%%%%#%#%###%###..='''''''''','~'!'~','~,!,,~,~,~!,,~,~~#///////////(u////wx(////////////////////////////////////,,'',',!~~!~!~~,~~~~,~!!!~~~~~,~~~~~!~~,~~~~~~~~~~~~~!~~~~!~!~~~!~~!'''''',#++++++++/ ", +" 000000000000000yyypjyoojyjjpjjojjjojjjjojojojjjjjjjjjjjajjjjjajajjajajaajaajaajaaaajaaaacaaaaaaaaabajjzyzyzzozyzzzzzj00000000000000b %-%-%%%%##########...#]'''''''''',~'~,'~,,'!!,!,~,,!,~,,!!,]+//////////////xwww(//////////////////////////////////-',''',~,!!~!~,~~,~,~~!~~!!~,~~~,~~!~!~~~~~,~~~~~~!~!~~~!~~~~~~~~~~~,''''''=++++$+++^ ", +" 000000000000000jyyypyjyjojojooojjojojjjjjjjjdadjjjjjjjjjajjjjajajajajjajaaaaajcaaaaaaaaaaaaaaaaazzzyzyzzyzzzzzjzj00000000000000b %%&%%-&% %#######.......%!''''''''''!'~'~,!~'!'~'~,~,~,!!,!!!~=>///////////(xwwx//////////////////////////////////>,',',''~~~!~!~!~~~~~~,~,~,~~~,~~~~!~!~!~!!!~,~!!~!~~~~~!~~~!~~~~~~~!'''''',#+++++++@ ", +" 0000000000000000jyvyyojyjjjojojjojjojojjjjjjjjjjjjjcjjjcjjajjajajaajaacjaaaajaaaaaaaaaaaaajozyzyzyzyzzzyzyzj000000000000000b %-%-%-%%- %####...#......-,''''''''''~'~',~'~,~'~',,~'~,!,,,,!,&(//////////(tw(//////////////////////////////////],''',,~~,!~!~,~!,~,~!~~!~~,~~~!~,~~~,~!~~~~~!~~~~~!~!~~!~!~~!~!~~~!,''''''&++$+++++/ ", +" 00000000000000000oyyyyyyojojojjpjjjjjjjjjjajjjjjjoajjjjjcjajajajaajjajaajaaaaaaaaaajjozyzyzyzyzyzzozyzj0000000000000000b -%%-%%&% #......$......#-'''''''''','~,,!','~,~~'~'~,!,!!!,!,~%//////////////////////////////////////////////.,',',',,~~!~!~!~~!~~~!~!~,~~,~,~~~,~~~!~~~~~~~~~,~!~~~~~~~~~~~~~~~~,''''',!++++++++@ ", +" 00000000000000000jyyypyyyoojjjjojojjjjjjjjjcjjajjajajjjjjcjjcjjaaaaaaaaaaaaajjjoyyzyyzyzyzyzyzyzzj00000000000000000b %%%-%%-%- .....#........#='''''''''',~'~!!,,,,'~'~'~!,,,!,!'!,~.////////////////////////////////////////////,'','',~~!!~,~!~,~!~,~~,~~!~~~~~!~~~,~~!!~,~~~~,~~~~~!~!~!~!~!~~~~!~,''''''%+++++$++/ ", +" b000000000000000000jyvyyvvyvooojojjjjjjjjjjjajjjjjajajjjajjaajjaajajjoooyoyyyzyyzyzyyzyzyzzzjj00000000000000000b -%-%-%%-% $...+#$.......%]'''''''''','~'~'~~,~,~'~,!!!,!,!,!!,*@/////////////////////////////////////////%,','',!,~~!~!~!!~!~!~,~~,~!!,~!~~,~!~,~~~!~!,~!~~!~~!~!~~~~~~~~~~~~,''''',=++$+++++> ", +" b0000000000000000000jyyyyyyvyvvyvooooojjjjjjajjjajjaajjjjjojooooyyoyyzyyyzoyzyzyzzyzyzj00000000000000000000b %%%-%%-%% .#+...#.......&!''''''''',,~'~''~'~'~'~'~'!'!,,'!,!,&>//////////////////////////////////////(''','',~~,!~!~!~~!~,~~~~!~!~~!~,~~~~~~!~,~~~~!~~~~~,~~~~!~!~!~!!~~~,'''''',#++++++++ ", +" )000000000000000000000ayyyyyyyyyyyyyyyyyyvvyoooyyyyyyyoyyyyyyyyzyyyyyyyzyzyzyyyzza00000000000000000000b %-%-%%%-% #+#....$.......-'''''''''''~,!~'~,~,~,~,~!!,,!!',,,,~%(////////////////////////////////////*','','!!~~,~!~!,~!~!~,~~!~!~!~!~,~~,~~~!~!!~~~~,~~!~~~~~~~~~~~~~~!~,''''',-++++++$+( ", +" b0000000000000000000000jjyyyyyyyyyyyyyyyyyzyzyyyyyyzyyyyyyyyyyyzyzzyzyzojj0000000000000000000000bb %-%%-%-%-% .$.#.#........#='''''''''''~'~'~'~'~'~'~,!'~,!,,',,,~#//////////////////////////////////@,','',,~~,~!~!~!~!~~!!~~,~~,~!~!~~!~~~,~~~~!!~~~!~~~~!~!~!~!~!~~!~~,''''''!.++$+.++> ", +" b0000000000000000000000000ajyyyyyyzyyzyyyyyzyzyyyyyzzzyzyyzyzozjaa0000000000000000000000000b %-%%-%%-%% #+.+#$..#.....#]''''''''''~'~'~'~'~,~'~!!,,,!'!,,,,,*+////////////////////////////////,,'',''!~~!!~,~!~~!,~~,~~!~!~!~!~~,~~!~~~,~!~~~~~~!~,~~~~!~~~!~~!~~!'''''',%++.+++++/ ", +" b0000000000000000000000000000jajjjjjzjyoyyyyyyzjjjjjaaa0a0000000000000000000000000000b %-%%-%%-%% ....#+........%!''''''''''~'~,~,~'~,~'~'!'!,!,,'!,,,->/////////////////////////////%','','~~,!~~!~!~,!~~!!~,~~,~~!!~,~~,~!~~~!~!!!!~~~~~!~!~~~~~~~~~~~~,''''''*++++++++> ", +" )b000000000000000000000000000000000000000000000000000000000000000000000000000000b %%-%%-%%-%% ....#$#+#.....-'''''''''''~'~',~,,~,~!~!!,,,!,,,,,,,&(//////////////////////////(,,'',',,~~!,~,~!~~!!~~!~,~~~!~,~!~,~~,~~,~~,~~~~!~,~~~~~~~!~!~~!~!!~'''''',#++.$.+$+ ", +" )b0000000000000000000000000000000000000000000000000000000000000000000000b) %-%-%%-%-%% .$.......$#...#-'''''''''''~!~'~'~'~',~'~,',,,,',',,!@/////////////////////////=,''','~~!!~~!~,~!!~~,!~~!~,~~~~~!~!~~~,~~~~~~,~~~~~~,~~,~~!~~~~~~~!,''''''-+.++++++( ", +" %)b00000000000000000000000000000000000000000000000000000000000000b) %%%-%%%-%%%% ...$..........#='''''''''''~,~,~'~~'~,~!~!',,,,,,,,,=(//////////////////////@,''','~,~~!,~,~!~~!!~~!!~,~~,~,!!~!~~,~~~~,~!!~!!,~!~~!~~!~~~!~!!~~,''''''!#++$).++@ ", +" %&%)bb0000000000000000000000000000000000000000000000000000bbb %-%-%%-%-%-% .#.#+.........%]'''''''''','~,{',~'~,,~!~'~',,,',,,,&/////////////////////!',,'',!~,!~~!~~,~,~~,~!~~!~!~~~~!~,!~!~~,~~~~!~~~~~!~~!~~~~~~~~~~~~'''''''%++++$+++/ ", +" %%%%%))bbb000000000000000000000000000000000000000bbb %%%-%%-%-%%%% .+#...$#.#....%,'''''''''',,,~'~,~'~,,~'~!',,,,,,'''#//////////////////#','''',!~~,~!~,~!~!~!~!~!!~,~~,~,~!~~!~,~~~~,~~!!!,~~~~~,~!!!~!~~!~,,'''''*++.+++.+> ", +" &&%-%%%%%%))bb000000000000000000000000b0bbb) %%-%-%%-%%%-%- ..$#..........#-'''''''''',!!~'~,~,~,~,~,~',,,',,,,,!@///////////////(,,'',,,~~,!~!~,~!~,~!~!~,~~~!~~~~~~,!~~~!~~,~~~~~~~!~!~~~~~~~~~!~~!!'''''''#+$+.++$+ ", +" %&%-%&%%%%%&%&&% )))) %%-%-%%-%-%-%-% ......$.#.....#='''''''''''~'~'~'~,~'~,~~!,,',,,,'',*>/////////////-''',''!,~~,~,~!~!~!~!!~!~!!!~,,~,~!~~!!,~~!~~,~,~!!~~~,~~!~~~~~~!~~,''''''-.+.+++++( ", +" %%-%&%&%%%%%&%%%- %%-%-%%%-%-%%%%% ..$#..........#]''''''''''~,~'~'~,~'~,,~!!,',,,,,',,-(//////////>,,,''',~~,~!~!~,~,~!~,~!~!~~!~~~~!~!!~~~!!~~~~~~~~~!!~~~~~~,~,!~~~~,'''''',.+++$.+.@ ", +" -%-%%&%&%%&-%%%-%% %%%-%-%%%-%-%%%-%- ..+#+#$....#..%!'''''''''''~~,~,!,{'~,~!!!,',,','',,%/////////!,'',,'~,~!~!,~!~!~,~!~~!~,~!~!!!~,~!~,!!~~,~,~!~,~!~~!~!,~!~~~~~~!~,''''''&+.+.++++/ ", +" %-%%-%-%%-%-%-%%-%%%- %%-%%%-%-%%-%-%-%%%-%- ..$...#........-''''''''',',~,~'!!!,~'~,~!',,,,,',,,,.//////#''''''~~~,~!~~,~,~~!~,~!~!~,~!~~!~~~,~~~,!~~~~~,~~~,!~!~~~~~!~~~,~~,'''''']+.+++.$+@ ", +" %%-%%-%%%%%-%%-%-%%%%-%%%-%% %%-%%%-%%-%%--%%-%-%%%%%-%- +#....$..#....#=''''''''''',~!!,~,~,~,~!~',,',,'''',!>////,,,,','!,~,~!!~!~!!~,~!~!!~!~!~,~,~,~~~!!~~!!,~~~~!~~~!~~~!~~~,~~~~!'''''',#++$.++++/ ", +" %-%--%-%-%%-%-%-%%--%%-%%-%%%-%%%-%%%-%%-%%-%-%%-%-%%%%-%%%-%--% ..#+#+.......+#]'''''''''',~,~'~,~,~,!~!!!',,,,,,','=(/-'''','~~~!~,~~'~!~~!~,~,~~!~!~~!~~~~,~~~,!~~~~~,~~~~,~!!~~~,~~!!~~~,''''''=..++.+.+> ", +" -%%%%-%%%%-%-%%%-%-%-%%-%-%-%%--%%-%%-%%%-%-%%%-%-%-%-%%% ..$.#+#$......%!''''''''''~,~,~,~,~,!,~!!,,'''',',',=','',',!,~'~!,~~~!,~!~!~!!~!!~,~!!!!~~,~!~~!!!,~~,~,~~~~~,~!~~~~~~~,'''''',#++#++$+@ ", +" %-%-%%%-%-%%%%%-%-%%%%-%%%%-%%-%-%-%%%-%-%-%%% ...$...#......-!''''''''''~,~'~,~,~~,~,!!,,,',,,',,'''','~~~~~,~~,~,~~,~,~~!~,~!~~!~!~!~~!~!!~~~~!~~~~!~~,~~!~~~~~~!!~'''''',&++++$)++^ ", +" %%%%--%-%%%-%-%%-%-%%-%-%%%-%-%%% #+#.....$.....#-''''''''''',~,~,~,~,~,~!~,',''''',',,''~,!,,~!,~!~!!~!~,~,~!~~!~,~!~!~,~~,~~!!!,~~~,~~~!~!!~~~,~!~~~,''''''!++$++.+.> ", +" %-%-%% ...$#.........#='''''''''',~,~,~,~!~,!!!~,,,',','',',!~~~~,~~'~!~,~!~!!~,~!!~~!!~~,~~,~~!~~~~!!,~~,~~~~~!!!~!~!~!!'''''''%+.+.+.++/ ", +" .....#$.......%]'''''''''''~,~,,~,~,~!!!,,,',','',,!,~'~!!~~~,~!~!~~!~!~!~,~!~,~~,~~~,~,!!!~~~~~~~,~,~~~~~~~~~~,''''',=++.+.$++> ", +" ........#.....%,''''''''''~,~,~,~,,~,~~,!','',',~~~!~!~,~,!~~,~,~,~,~~,~~!!~!~~~,~~~~~~~,~,~!~,~~~!~,!!~~!~!,'''''',#.+.++.++ ", +" +#+#...$.......-'''''''''''~,~,~,~,~,!~!!!'','!,,~,~,~!~~!,~!~!~!~!~!~,~!~!~!!~~,~,~,!!~!~,~~~~,~~!~~~!~!~~,''''''-+++$+.++( ", +" ..$#..........#='''''''''',~,~,~,~,~,~!!~,'!~~~!~,~,~,!~~,~!~,~,~,~~!~!!~,~!!~~~~~~~!~!~~!~~~~~~~!~~,~~~,''''''!..+.+.$+@ ", +" .....#$#......#*'''''''''',~~,!~,~,~~,!~~,!,~,~!~!~~!,~,~!!~!~!~,~!~!!~~!~~,~,~,~!~!~!!,~,~,~~,~!!~~!~'''''',%$++.+.++/ ", +" ..$...........%!''''''''''!~,~,~,~,~,~,~~,~~'~~,!'~~!~,~~,~!~!~,~!~!!!~,~~~~~~,~~,~~~~~~~!~~~~~!~!~,''''''=.+..+.+.> ", +" .....#$+#.....-'''''''''''~,~,!~,~!~!,~~'~~,~~~~'~,~!,~!~!~!!~~!!~!~!~!!,~,~~~!~!!!,~~,~~,~!~~!~!'''''',#.++.$+++ ", +" ..$......#.....-'''''''''',~,~!~,~,~,~,~!,~'~,~~!~,~~!~,~,~~,~!~~,~!~~~~~~!,~,~~~~!!~~!~~~,~!~!,''''''-.+#++.++^ ", +" #...#$......#.#='''''''''',~,~,~,~!~,~~!~~~!~,~!~,~,~!!~,~!~,!~~~!!!!!,~~~~~!!!,~~~,~~!~~~!~,''''''!#+++$)..@ ", +" ........$.....%]'''''''''',~,!~,~!~'~,~,!~!~'~!~!~!~~!~,~!~!!!!~~!~~~,~,~~~~~!!~~~,~,~~,~~'''''''%+.$..$++/ ", +" +#$...........%!''''''''''~,~'~,~,~!~~,~,~~~!~,~!!,~!~,~!~!~!!!~!!!~~~,~!!,~~,~~~~~!~~~,''''''~+..+.+..> ", +" ..#.#.#.......#-'''''''''',~~~,~~,~,!~!~,!!,~!!~~~,~~!~,~!~!~!~!~~,~~!~~~!!~!~,~~!~!!,'''''''#+.+.+.++ ", +" .+.+.+#$.#....#='''''''''''~~,~'~~,~,~~,~~,~!,~!~!,~!~~!~,~!~,~!~,~~,~!~~,~!~,~~!~~,''''',-#+.+#+..( ", +" .$#...........#]'''''''''''~~~,!~!~,~~,!~,~~,~!~~~,!~,~~!~~!~!~~!~~,~,~!~~~~!~!~,'''''',.+$.++$++ ", +" .+#..#$....#..%!'''''''''',~~'~,~!,~!~!~,~!~!!,~~,~~,~!!!~~,~,~!~~!~!~!~,~~~!~'''''''&+#+....+/ ", +" .+...#+.......-'''''''''''~~!~,~!~,~,~!!~,~~~,~~,~~!~!~,~~~~!~!~!~!~!~~,~~~,''''''].++.$+.+> ", +" +#....#$.......=,'''''''''~'~!~,~,~!~~!!~,~!~!!~~,~!~!~!!,~,~!~~,~!~,~!~!!'''''''#..$).+#+ ", +" .$.....#......#]''''''''''~,~~!~,~,!~~!~,!~!~,~~!~!!~~~~~~~,~~!~!~~,~~~,''''''=...+.$+.^ ", +" #....+#$......%]'''''''''',,~!~,~!,~,~~!!!!~,~,~!~!!,~!,~~!!~~!!~!~!,'''''',#+.+...++ ", +" .$#+.#..#.....&,''''''''''~,~~,~~!~,~!~~!~!~!~,~~~!~~~,~~,~,~~,~!~'''''''&+......+/ ", +" ......$........-'''''''''',,~!,~!!~,~,~!!~,~~!~!,~!!~~,~~~!~!~!~,''''',!...+$+.+@ ", +" ..#+#+#+#...#.#='''''''''',~~,~~!~!~,~!~~!~!~~!~!~,~~!!~!~!~!!'''''''#.+#+#+.+/ ", +" .....$........%]''''''''''!!,~!~!!~~!!!!!~,~!~!~~,~~,~!~,~!,''''''*..+$)$.+> ", +" .#+#..........%!''''''''',~,~,~!!,~!~!~~!~,~!~~!~~~,~~~!,'''''',#+......+ ", +" ..+#$..$.......-'''''''''',~!~~!~~,~!!!~!~~,~!~,~~~!!!~'''''',-.+...+$+( ", +" ...#..........#='''''''''',!~,!~!!~!!~!!~!!~~,~,~~~!,''''''!..$.+...@ ", +" ....#.$#......#*''''''''',,~~!~,~,~~!~!~,~!~~~,~!~'''''',%.......+/ ", +" ........$.....%!''''''''',,~!~~~,~!~,~~!~,~~!~~,''''''*.+.+$.+.> ", +" #+#+#.........&'''''''''',!!!!~~!~~!~!~!~,~!,'''''',#.+#.)$.+ ", +" ..$..#..#......-'''''''''',~,!!~,~!!~!~~!~~'''''''-...+...+^ ", +" .....$........#='''''''',,,~~!~!~!~,~,~~,''''''!#.$.$...@ ", +" .....#..#..#..%]''''''''',,~!~,~~~~!~,'''''''%+.....++/ ", +" #...$.........%!''''''''',,~~!!!~,~,''''''*......+#> ", +" ......$.......#-'''''''',',!~!~~!~'''''''#.+#+.$.+ ", +" #+#..#$+#.....#=''''''''',!,~!!'''''''-#$.+....( ", +" ..............#]''''''''',,~,'''''',....#+#++ ", +" .+#..#$.......%!''''''''','''''''%.$).$+.+/ ", +" .$............-,'''''''''''''']......#+@ ", +" +#+#..#$#.....#-'''''''''''''#..$+#+.+ ", +" ..$...........#]''''''''''=.....+$#( ", +" +#...#$.......%]''''''',##+#+#..@ ", +" ....#...#.....&,''','&+....+.+/ ", +" $.+....$.......-'''=..$)$..#@ ", +" .$#...........#%%..#+.#+.+/ ", +" .+#.#$..#....#.....+#$.> ", +" +...#+..............+ ", +" ...$.#$..+#+#$..../ ", +" ......#..$..#+#+> ", +" +#.....#+#+..> ", +" ..$#+....$ ", +" ).$)++ "}; diff --git a/libimage/error_small.xpm b/libimage/error_small.xpm new file mode 100644 index 0000000..5c61f2a --- /dev/null +++ b/libimage/error_small.xpm @@ -0,0 +1,183 @@ +/* XPM */ +static char * error_small_xpm[] = { +"150 115 65 1", +" c None", +". c #5F605E", +"+ c #505352", +"@ c #454745", +"# c #727271", +"$ c #7F807F", +"% c #909192", +"& c #C3C3C6", +"* c #FDFFFC", +"= c #F4F2F6", +"- c #2E2F2D", +"; c #E8E6EA", +"> c #DFE0EA", +", c #D6D7DF", +"' c #7B9FC3", +") c #5A7EA3", +"! c #A8A9AD", +"~ c #050C19", +"{ c #081933", +"] c #071F5B", +"^ c #266BF5", +"/ c #2E79F5", +"( c #3684F5", +"_ c #4699FC", +": c #5589C0", +"< c #3C5C7C", +"[ c #2E5B9A", +"} c #1558F0", +"| c #003CEC", +"1 c #000300", +"2 c #5BAEFD", +"3 c #002BA8", +"4 c #5088D2", +"5 c #272D33", +"6 c #9B0300", +"7 c #A93937", +"8 c #D05D5A", +"9 c #FC7D6D", +"0 c #FC8E7F", +"a c #FC6E5D", +"b c #FB6354", +"c c #BD2221", +"d c #71A9DD", +"e c #FDA99D", +"f c #D02D2B", +"g c #88CAFF", +"h c #F39C93", +"i c #F7B9AD", +"j c #FC584D", +"k c #8FB6D9", +"l c #191B19", +"m c #991E1E", +"n c #07369F", +"o c #312E09", +"p c #FB4B44", +"q c #585310", +"r c #968E1B", +"s c #E83935", +"t c #92555F", +"u c #FC413C", +"v c #7D4153", +"w c #872A38", +"x c #DC2020", +"y c #F92C2D", +"z c #A5D7FE", +" ", +" ", +" ", +" ", +" ", +" .............+..+.++.++++++++++++++++++++++@+@+@@@@@@@@@@ ", +" ..#$%$$$$$$$$$$$$$$$$$$$$$$$$$$$$#$#$#$####$#$#$#$$#$###@@ ", +" ..&******************************************=****=****=$@- ", +" ..&**==*=*==*======================;=;=;=;=>==>=;=;=;;=*%@- ", +" ..&*=**=*=**=*==============;=;;;;;=>=>=>=>;;>;>;;>>>;;*%@- ", +" ..,*============*======;=;==;=====>=;=>=>=;=;=>=>;=>=>==%@- ", +" ..&***,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,>>=*%@- ", +" ..&*==,&%%%%%%%%%%%%%$%%%%$%%%$'$%%%%%%%%)%%%%%%%%%,,>;*%@- ", +" .#&**>,!~{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{]{]{]{{~,,>=*%@- ", +" ..&*==,%{^///((_(_____:@@<[__(_(///^^^}}}|||||||||1,>>;*%@- ", +" ..&*==,!~/((_______2:<@@@@@[[____((//^^^}}}||||||3~,,>==%@- ", +" ..,*==,%{((____2222<@@+!;.@<<4____(((//^^}}}||||||1,>>==%@- ", +" ..&*==,!~(____222<@@@%;**&@@<[42____((//^^}}}||||31,,>;*%@- ", +" ..&*==,!~__2222)+@+#;*=;==#@+):22____((//^^}}|||||1>>>==%@- ", +" .#&*==,!~__22:+@+.&**;>>;*,@5<::222___((//^}}}|||31,>>;*%@- ", +" 6666666666 ..&*==>!~222<+++!**;;;;>;=*%@@<:222____(///^}}}|||1,>>==%@- ", +" 66666666666666666666 ..&*=>>!~2<@++%=*=>=>=>>>>*=+-<):2222__((//^^}}||31>>>=*%@- ", +" 6666678980009999aaabcc66666 ..,*==>!5+++$;*=;;>>>>,,>>=*&@@<)d222_2_((//^^}}||1>>>;*%@- ", +" 666680e0000909999a9aa999afc6666 ..&*=>!+++#,**;=;=;>,>!&,;>==.@@):g2222__(//^^}}}31>>>==%@- ", +" 6668hhhh0h0000909a9aa9aaa99a9abc666 ..&*&#++.&**;;>=>>,,&<1#,,>;*,@@<):2222___(/^^}}|}1>;>==%@- ", +" 6668eeheh0e00009999aa9aa9aaaa9aa9abc666 ..&%+++!=*=>==>>,,,$1]3~!>>>=*$@@):g222___((/^^}}31;>>;*%@- ", +" 6668ieeeh0e000000999a9aa9a9a9aa9aaa9aabc666 .++++%;*=;;;>;>,,%{{3||]5>>>>*=+@<)'2222__((/^^}}}1>>>==%@- ", +" 668eieeee0h0h0009999a9a9a9aa9a9aa9a9a99aajc66 +++$,=*;;==>>,,!@13|||||~%,;>;*!@@<:dg222__(//^}}31>;>;*%@- ", +" 66hihieehe0e00000999aaa9aa9aaa9a9aaa9aa9a9abf666 +++#&**===>=>,,k.~]|||||||3l,>>>**.@+):2222__((/^^}}1=>>==%@- ", +" 6mieieeee0e000099999a9a9aa9a9a9a9a9aaaaaaaaa9aj666 ++..!====;;;>,,,#1]}|||||||||{#,>;;*&@@<<)222__((/^^}31>;>;*%@- ", +" 667eieihehh0h00000999aaa9a8aaaabbbbbbbbbbbbbbbaaajc66 +.++%=**=;==>>,,%~{3}}}||||||||3~&>>;=*$@-11)222_((/^^}}l>>>==%@- ", +" 668iieihehehh00009999a9a9aaaabbbbbbjjbbbjbjbjjjjjjjjc66 .++%;**=;==;>,,!5~n^^}}}}}}||||||]+,>>>*;++o1<22___(//^^31=;>>*%@- ", +" 66hieihehh00000009999aa9abbababbbbbbjbjjbjjjjjjjjjjpjjf66 ++.#&**===;;>,,k+~]/^^^^^^^}}}}|||||~!>;>=*!@@1<422_((/^^^}1=>>==%@- ", +" 68ieieeeeee0h0009999aaabaabbbbbbbbbbbbbbjbjbjjjjjjjjppjf66#.+..&**==;==>>,&#l]////////^^^^^}}}}||n5,>>,*=.@-<:22__(/[]]nl>;;>*%@- ", +" 67iieeeeh00h00009999aaaababbbabbbbbbjjjjbjjjjjjjjjpjjjpjpf6m#.!=======>>,,#~{((((((((/////^^^}}}}||~$>;;;=&@@5<<[4((n1q~n1=>>==%@- ", +" 6meiieeehee000090999aa8bbababbbbbbbjbbbbjjbjjjjjjjpjpjpjpjpc67&;*=====>!$+5l][4__(_((_(((////^^}}}}}31,>>>=*#@5111<_([1r1n~;;>==%@- ", +" 6ceieeeehh0h0000999ababbaabbbbbbbbbbjjjjbjjjjjjjjjjjjpjppppps66t;,===>,&+@@@@<[[[________(((////^^}}}|].;;;;*;@@l11[((/111nl=>>;*%@- ", +" 66hieihee0e00000999aaaaabbabbbbbbbbjbbbbjjbjjbjjjjpjpjjpjjppppp66i&;>,,k+@.!+@@@<[[442_______((//^^^}}}3~!>>>;=%@-o1{_/[111n1=;>>*%@- ", +" 68ieeeehhe0000099aaaababbbbbbbbbbjbjbjjbbjjjjjjjsjpjppjppppppppf67!&&,$5@@>==!@@@<<)4:222_____(((//^^}}}n-;>>>*=.@@1{([{~o1]1=;>;*%@- ", +" 6mhiieee0h0000099aaaaabaabbabbabbjbbbjbjjjjjjjjjjjjjjjjjppjppjppp66%!!5{+@%====>%@@@<<<:42222____((///^}}}~%=>;;*&@+l{(~11r111=;;>*%@- ", +" 68eehehh0e0000999abababbbbbbajbbbbbbbbjbjjjbjjjjjjjppppppjpjpppups6m#@)[++==;>==*,#@@@<<:::222____((//^^}}n~;>>>=*#@-{[{1111~1==>>*%@- ", +" 6ceeee0e0h000099abaaababaaaabbbbbbbjjjjjjbjjjjjjjpppjjjjjpppppppppuc6v::.@&*=>=>;=**&.@@@<)::2222___((//^^}}{#=>;;=;++{[~11111l=>>;*%@- ", +" 68ieehe0e000099abaabababbbj8jbbbbjbbbbbjjjjjjsjjjjjpppppppppppuppuus6m'%.#*=>>;>;;;===!+@@@<)):222___((/^^}^3~&=>;;*%@@11111111==>;=%@- ", +" 6meehehh0000999aaaaabab8abbjbbbbjbbjjjjjjjjjjjjjppjjjjppjppppppppupuuc6v##&=>=>,,,>>;==*;%@@-<<)::22___((//]1]]@;;>;*=+@51111111=>>>*%@- ", +" 68eeh00e000009baababbbbjbbbbbbbjbjbbjjbjbjjjjjjjjppppjppppjupppupppuuu6m#%,,;;>,!,,,>;;==*,$@@@<<<:42___(/{lr1n~!>>>;*!@@1111111==>>*%@- ", +" 6cheee0h000999aba8abaaabbbabbbjbbjbjjbjjjjjjsjjpjjjjjpppjpppuppppuuupsuc6t&&&>>,,~5!,,>>>>=**&.@@@l{::___((/1o1~nl;=>>=*#@-111111=;>;*%@- ", +" 6che0e0000099ababababbbabbbbbjbjbjbjjbjjbjjjjjjpppppjpjppppppupupppuuuuf6w&&&,,,.{3~@&,>>>;;=*=!+@@@<<:44(((]111]{#=;>;*,@@l11111==>>*%@- ", +" 680eh0000099aaaaabababbbbbbbjbj8jbjbbjjjjjjjjppjjpppspuuupupupupuuuuuuus66&&&&,!~3||]~.,>>>>;==*=%@@@5<{1<_(/~1o1~1,=>,=*%@-11111=;>;=%@- ", +" 66hh000000999aaaabaabbabbbbjbbbjjjjjjjjjjjjjpjspuuuuuuuuupuuuuuupuuppuuuu66!&!&&5]|||||]1$>>>>>>==*;$@+@l1~[(({1r11{+==,>==+@l1111==>>*%@- ", +" 6c0e0h009999bbabbabbbbbbbjabbjbjbjjjjjjjjjpspupusuppupusuusuuuuuuuuuuuuuux68&&!!~|||||||3{5!>>>>>>=*=,#@@@l{[]111111~%=>>;*!+@$!,*=;,;*%@- ", +" 68000000999aaaaabbbabbbb8jbjbbjbjbbjjjjjssppuuuupuusuuusuuuusususuxusuuups67,!!@n|||||||||31@&>>>>>>==*&+@@-<[~1111111==>>*=#@+%!;=;>>=%@- ", +" 6690h09aa9abaabbab8babajjbbbjbjbjjjjjjjjpppupppppuppuspuppsusuusuuuusuxusyu6m&&%+3|||||||||||]~.,;;;>>;===!@@-<{1111111@=>>>=,@@.%&>>>;*%@- ", +" 6600009aa9aaa8aaaabaaabaabababab8jbbabbjjjjjjjjjjpjjjjjjjpjpjppppppsuuuusyuc6!!+[^|||||||}|||||]1$>;>>>>;*=;%@@@-1111111&=>;;*$++$%,,>>*%@- ", +" 6690099b0iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii8suxusyxct.v4[}}}}}}}}}}}}|}3{l!>=>>>>==*,#@@@l111115==,;=>@@+%!,,;*%@- ", +" 6600099b0,,,,,>,>,,>,>>>>>>>,>>>>>,,,,,>>>;;;,;,;,;;;;,;;;;;;;;,;,;;&xuxusux6t@([4/}}}}}}}}}}}}}|}n~-&=;;;;;;**&.@@-l1111%*>,=*!@@#%,,>=%@- ", +" 660999aa0,;;*******=*==*===*=*=*************************************hssssxuf6w<(4[4^^^^^^^^^^^}}}}|}]~.,;>>>;>===!+@@-11l!;=>>==.@+$!,;*%@- ", +" 6c0999aa0;;********************=*=**=*******************************hfususyx6w[([:(/^/^//^^^^^^^^^}}}3]1$>=>;>>;=*=%@@@+$%!!,>>=&@@<%&>=%@- ", +" 6c9999aa0;******************************=***************************hxsxsxux6w(44)4(///////////^^^^}n1]3{l!==;>>>;==;$@@@.#%%&,==$@+$%>=%@- ", +" 6c99a9ab0;**********************************************************ifsssuuy6w444'44/(_((((((/(//^^/1ql1]n1-&==;>>>;==&#@@@.$%!&=;@@.%&=%@- ", +" 6c99aaab0;********************************************************==hssxxsyy6w(44':4(_(___(_((((//(n1q11111{~+&==;>>>===!++@+.%%!=!@+#%;%@- ", +" 6ca9a9ab0**********************************************************=hfssssxs6w444'4d__________(_(((/({11lq111~1-;==>>,>===!@@@+#%!&+@.%&$@- ", +" 669aa9ab9=**********************************************************hfxfssyxcw4'4':d_2_222_2_____(((//n~lo111~111.===>>>>==>$@@@.#!%5@#!$@- ", +" 66a9aaab0*=*********************************************************ifsxfxux6v_dd''d2222222222_____(((({11111111111%*=,>>>>==,#5+@.%@5@%.@- ", +" 66a9a9ab0*********************************************************=>hfsxssyfct4:d''d22222222222222{~4((]11111111111%=>>>>>>>===&v@@+#--++-- ", +" 66aaaaab9=**********************************************************hcffssyc6)ddd'kk2gggg2g2~<222:111{4/({11111111-==>,,,>,>>>=*=!@@@@5--5- ", +" 66c99abb0**********************************************************=hffsfyy6c'ddk'dk2g22ggg<1o~<2~11ol1{(/11111111&=,>,>>>,,,>,>==;%@@@-5ll ", +" 6fa9abb9***********************************************************hffxfyy66:kdk'kkggggg2d1lr111111qo111{1111111+==>>,,,>>,,>>>,>==,#@@-111 ", +" 66aaabb0*=********************************************************=hfffsyxcwddkk!kdgggggggd51qql1111111111111111==>,>>>>,>,>,,,>>>;==.@-l11 ", +" 66a9bbbb888888888888888888888888888888888888888888888888888888888887ffxuyx6tdkdkkkkgggggzggg'51qql1111111111111$*>,>,>,,>>,,>>,,,>;*,@@-l11 ", +" 66cabbbbbbbjbjjjjjjjjppuppspspsssssssssssfsfsffffffffffffffffffffffffffyy66'kkkk!kgzgzzzgzgggg)11r111111111111l==>>>>>,>,>,>,,,>,>==#+-l11 ", +" 6cabbbbbjbjbbbjjjjjpupuuuspspspssssfsfssfsfsfsffsfffffffffffffffffffffyx6mdkkkk!kzgzggzz'gzgggg<1111111111111&=>>,,>,>>,>>,>>,>,=*!@@l111 ", +" 66bbbbbbbjjjjjjjjjjppupsppspspssjsjssjfsfjffjfsfffffffffffffcffcfffffxyxcvkkkkkkkzzzzzzg11)zzgggl11111111111@==>>>>>,>,>,>,,,,,>==.@-11 ", +" 66fbbjjbbbjjjjjjjjjpppppsuspssssfsffjfsfffsfffffbfffffffcfcffcfffcfffuy66'kkkk!kzzzzzzz+~115'zg<111111111111;=>,>>>,>>,>,>,>>>,=*%@@11 ", +" 6cjbbjbjjbjjjjjjppuusupspssjssjspsffjfjfbfffbffffffffffcffcffcfcfcfxyx6mkkkkk!kzzz>>zz511qq15'111111111111#*>>>,>>>,>,>,>>,,,>=;+@l1 ", +" 66fjbjbjjjjjjjjjjppupuspspssssffjfjffffffffffcffff8ffcfffcfcffcfcffyyc6)kkkk!k&z*;==;;z$~11~11~1111111111l==>>>>,>>,>,>,>,>>>=*#@@1 ", +" 6cbjbjbjjjjjjjjppupsspssssjssjssfsfsfjffbfffcbcfffcf8cccfcfccfcfcsyx6mkkkkz!k&;;*>;*==>5l111r1lr11111111!=>>>,>>>,>>>,>,>,,;*&@@l ", +" 66fjbjjbjjjjpjjppuupupsjsssfsfsfjfjffffffff8fcf7ffcfcff7fccfcccffyy66)kkkk!kk;=>==;===!~r151o1111111111-*=>>>>>>>>>,>,>,>>>*=.@5 ", +" 6cjjjjjjjjjjjpppspsssssspsssjsfffsffjfffffffffffcfccffcfccccfccxyxcwkkkkk!k&;;*;=====#1ooq111111111111;=>>>>,>,>,>,>,>>,,==!@+1 ", +" 66cbjjjjjjppjjpuupuppssssjfsfssjffjfffbcfffcffcf7ffc7fcccfccccfyy66'kkkkk&k,*;*;=*;*=*&5loqol11111111.*;>>>>>>>>>,>>>,>>>=;+@- ", +" 66jjjjjjpjpjppsussssjssssfjsffsfffffffffffff7fff7cffcf7f7cccfyyx6vkkkkkk!kz===*>===**>z'~oo111111111==>>>>>>>,>>>>,>,,>=*$@@ ", +" 666jjjjjjjpjpppuppsssssjfsffjffjff8fff8ff8cffcfcfccc7fccccccuyx66'dkkk&kkz>*==***=*==zzzz5~11111111!*>;>>>>>,>>,>,>>>,;*&+@l ", +" 66cjjjjpjpjppspsspsjssspfjfsfffffffffcfffffcfcccfccccccfmcuyx66)dkkkkk!kzz>=====*=**>zz'111111111-*=;>>>,>>>>>>>,>,>>**.@@ ", +" 66fppjjpjpppspspsssssfsssfsjffjffffffccccf7fcf7cccccc7ccsyy66vddk'kk!kzzzz****=***zkzz-111111111,=>>;>>>>>>,>,>>>,>==!+@1 ", +" 66fjjjpjppuususssssfjffjfffffffffff8fffcff7ccf7cc7ccccfyyc6vddddkk!kkzzzzz=*+!**z%1-<111111111+*;;>>>>>>>>>>>>,>>>*;++- ", +" 66spjppjpsususpsssssssffjfjffff8ffcf7fccffccccccccmcsyyx6m5+)ddk'kkkzzzzzz'115'zlol1111111111==>;>;>>>>>>>>,>>,>=*%+@ ", +" 66fpjppppspssssjssfjfsfffffffffcfffffcf7cccf7cmcccxyy66m&&$@+''kkkggzzzzzlq1115lq1q11111111%*;>>;>;>>,>>>>,>>>>*&++l ", +" 66sjpjuusppssssfssffsfjfffffffffccc7fcfc7ccccc7cuyyc6m!,&&!#+$'kggggzzzklqlq11111r1111111l==;>>>>>>>>>,>>>>,>=*#+@ ", +" 66cppppssspssssjfssffffs8ffffcf8cffccccfmfmccfyyx667&,&,,&&!.@>>>;>;>>>>>>>>>>=*!@+1 ", +" 666ppuusssssssfsfssfsfffffcfcfcfccc7fmfcccmsyyx66w&;,,&,&!&&%5l'ggzggzgk1511qo11111111@==;;>>>>>>>>>>>,>,>*=.+- ", +" 666sspspssssssjfjfffsfff8fffcf7ccfcccc7cfyyyx66t$#%&,,&&&,,>>$~5dggzgz51115ol11111111==>>>;>;>;>>>>>>>>>=*%+@ ", +" 666cxsspssssfsfffjfffffffcffffccc7fmffuyyx666 #$#$!&&&,,=;>;,.1;>;>;>>>>>>>>>>>*,++- ", +" 666fssssfssfssfffffffff7fccc8ccccfsyyx666m $$#$%!,======;=&@l;>;>>>;;;>>>>>>>**#+@ ", +" 666csssssffsffsfffffffffccccfsuyyx666m %$##$;**===;==>!-~~q1q1111111111&*;>>>;;>>>>;>>>>>>;*!++1 ", +" 666cfssssfsffffffffcccfssuyyxx666m %%% $...%;*====;===%lllq111111111@==>;;;>>;>>;>>;>>>>*=..- ", +" m6666ccspsspsfsffsspsuyyxc6666mt %%! ....!**===>===>$15111111111;=;>;>>;>;>>>;>>>>>=*%++1 ", +" tm66666ccffsssssxfxc66666m7 !% ....&*=*=>=;==,.11111111#*=;;;>;>>>;>;>>;,;;*;.+5 ", +" twm666666666666666mmw %%% ...$&**==>=>==!111111l==>;>;>>;;>>>>;>>>>=*#.@ ", +" %twwmmmmmm7w %%%! ...%;**=;=>===#1111!*;;;;;;;;>>;>>>>;>=*&++l ", +" %!%%% %%%!% ....!=**;=;====+1-*=>=>>;>>;>>;;>;>>>*=#+@ ", +" %!%%%%%%%%%%%!% ....!**==>;===,>=;>>=>=>;>;;>>;>>;==!++1 ", +" ...#&**=;;;=*==>=>>>>;;>;>>>>>>;*;.+- ", +" ...$,**=;;;==>>=>=>;;;>>;;>;;=*$++ ", +" ....%=**;>>;;>;>>=>>;;>>;>>=*&.+l ", +" ....!=*==>=>=;;>;;>;;>>>>==#.@ ", +" ...#&**=>>;;>;;>;>;;;;=*!.+1 ", +" ...$,**=>=>=>;>;>>>;*;..- ", +" ...%;*==>>=>;;>=>=*%.+ ", +" ....!=*=;>>;>>>=*&..l ", +" ...#&=*=;;;>>**#.@ ", +" ...$&**=>;=*!..1 ", +" ...%;*==*=..- ", +" ....%=**%.+ ", +" ...#!&..5 ", +" .....@ ", +" ..+ "}; diff --git a/libimage/img1.xpm b/libimage/img1.xpm new file mode 100644 index 0000000..02143eb --- /dev/null +++ b/libimage/img1.xpm @@ -0,0 +1,56 @@ +/* XPM */ +static char * img1_xpm[] = { +"37 50 3 1", +" c None", +". c #000000", +"+ c #FFFFFF", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ........... ", +" .....+++++++++. ", +" ...+++++++++++++. ", +" .++++++++++++++.. ", +" .++++++++++++++. ", +" ..+++....+++++++. ", +" ...... .+++++++. ", +" .. .+++++++. ", +" .+++++++. ", +" ..++++++.. ", +" .+++++++. ", +" .+++++++. ", +" .+++++++. ", +" .+++++++. ", +" .++++++.. ", +" ..++++++. ", +" .+++++++. ", +" .+++++++. ", +" .+++++++. ", +" .+++++++. ", +" ..++++++.. ", +" .+++++++. ", +" .+++++++. ", +" .+++++++. ", +" .+++++++. ", +" .+++++++. ", +" ..++++++.. ", +" .+++++++. ", +" .+++++++. ", +" .........+++++++......... ", +" .+++++++++++++++++++++++. ", +" .++++++++++++++++++++++.. ", +" .++++++++++++++++++++++. ", +" ..++++++++++++++++++++++. ", +" ......................... ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/libimage/img2.xpm b/libimage/img2.xpm new file mode 100644 index 0000000..f4046e4 --- /dev/null +++ b/libimage/img2.xpm @@ -0,0 +1,56 @@ +/* XPM */ +static char * img2_xpm[] = { +"42 50 3 1", +" c None", +". c #000000", +"+ c #FFFFFF", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" .......... ", +" ......++++++++.... ", +" ...++++++++++++++++.. ", +" .+++++++++++++++++++.. ", +" .++++++++++++++++++++.. ", +" .++++.........++++++++. ", +" .+.... ..+++++++.. ", +" .... ..+++++++. ", +" . .+++++++. ", +" .+++++++. ", +" .+++++++. ", +" .++++++.. ", +" ..++++++. ", +" .++++++.. ", +" ..+++++.. ", +" ..++++++. ", +" ..++++++.. ", +" ..+++++... ", +" ..+++++.. ", +" ...+++++.. ", +" ..++++++.. ", +" ..+++++... ", +" ...+++++.. ", +" ..++++++.. ", +" ..++++++.. ", +" ..++++++.. ", +" ...+++++... ", +" ..++++++.. ", +" ..+++++++. ", +" ..++++++++............... ", +" .+++++++++++++++++++++++. ", +" .+++++++++++++++++++++++. ", +" .+++++++++++++++++++++++. ", +" ..++++++++++++++++++++++.. ", +" .+++++++++++++++++++++++. ", +" ......................... ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/libimage/img3.xpm b/libimage/img3.xpm new file mode 100644 index 0000000..c83fa64 --- /dev/null +++ b/libimage/img3.xpm @@ -0,0 +1,56 @@ +/* XPM */ +static char * img3_xpm[] = { +"42 50 3 1", +" c None", +". c #000000", +"+ c #FFFFFF", +" ", +" ", +" ", +" ", +" ", +" ", +" ............. ", +" ....+++++++++++.... ", +" .+++++++++++++++++... ", +" .+++++++++++++++++++.. ", +" .++++++++++++++++++++. ", +" ..++.........+++++++++.. ", +" ..... ...++++++++. ", +" . ..+++++++. ", +" .+++++++. ", +" .+++++++. ", +" .++++++.. ", +" .++++++. ", +" ..+++++.. ", +" ..+++++.. ", +" ...+++++.. ", +" .........+++++... ", +" .++++++++++++.. ", +" .++++++++++++. ", +" .++++++++++++... ", +" ..++++++++++++++.. ", +" .........++++++++.. ", +" ...+++++++.. ", +" ..+++++++. ", +" .+++++++. ", +" .+++++++. ", +" .+++++++. ", +" .+++++++. ", +" ..++++++.. ", +" . .+++++++. ", +" ... ..++++++.. ", +" .+.... ....+++++++. ", +" .++++..........+++++++++.. ", +" ..+++++++++++++++++++++... ", +" .+++++++++++++++++++++.. ", +" ..++++++++++++++++++... ", +" .....++++++++++..... ", +" ............ ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/libimage/img4.xpm b/libimage/img4.xpm new file mode 100644 index 0000000..3be99a9 --- /dev/null +++ b/libimage/img4.xpm @@ -0,0 +1,56 @@ +/* XPM */ +static char * img4_xpm[] = { +"44 50 3 1", +" c None", +". c #000000", +"+ c #FFFFFF", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ........... ", +" ..++++++++.. ", +" ..+++++++++. ", +" .++++++++++. ", +" ..++++++++++. ", +" ..+++++++++++. ", +" ..+++++++++++.. ", +" ..+++...++++++. ", +" ..+++.. .++++++. ", +" ..+++.. .++++++. ", +" ..+++.. .++++++. ", +" ..++++. .+++++.. ", +" .++++.. ..+++++. ", +" ..+++.. .++++++. ", +" ..+++.. .++++++. ", +" ..+++.. .++++++. ", +" ..+++.. .+++++.. ", +" ..+++.. ..+++++. ", +" ..+++.. .++++++. ", +" ..+++.. .++++++. ", +" .++++. .++++++. ", +" ..++++. .++++++. ", +" .+++++............++++++...... ", +" .+++++++++++++++++++++++++++. ", +" .+++++++++++++++++++++++++++. ", +" ..+++++++++++++++++++++++++++. ", +" .++++++++++++++++++++++++++++. ", +" .................++++++....... ", +" .++++++. ", +" .++++++. ", +" .++++++. ", +" .+++++.. ", +" ..+++++. ", +" .++++++. ", +" ........ ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/libimage/img5.xpm b/libimage/img5.xpm new file mode 100644 index 0000000..9aa80d3 --- /dev/null +++ b/libimage/img5.xpm @@ -0,0 +1,56 @@ +/* XPM */ +static char * img5_xpm[] = { +"43 50 3 1", +" c None", +". c #000000", +"+ c #FFFFFF", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ....................... ", +" .++++++++++++++++++++.. ", +" .++++++++++++++++++++. ", +" ..++++++++++++++++++++. ", +" .+++++++++++++++++++++. ", +" .+++++++++++++++++++++. ", +" .+++++................. ", +" .+++++. ", +" ..++++.. ", +" .+++++. ", +" .+++++. ", +" .+++++. ", +" .+++++.......... ", +" ..++++++++++++++... ", +" .+++++++++++++++++... ", +" .+++++++++++++++++++.. ", +" .++++++++++++++++++++.. ", +" .++..........+++++++++. ", +" ..... ..++++++++.. ", +" .. ..++++++++. ", +" ..+++++++. ", +" .+++++++. ", +" .+++++++. ", +" .+++++++. ", +" .++++++.. ", +" .++++++. ", +" ..++++++. ", +" . ..++++++.. ", +" ... ..+++++++. ", +" .+..... ...+++++++.. ", +" .+++++.........++++++++.. ", +" ..+++++++++++++++++++++.. ", +" .++++++++++++++++++++... ", +" ...++++++++++++++++... ", +" .....+++++++++.... ", +" ........... ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/libimage/img6.xpm b/libimage/img6.xpm new file mode 100644 index 0000000..e915233 --- /dev/null +++ b/libimage/img6.xpm @@ -0,0 +1,56 @@ +/* XPM */ +static char * img6_xpm[] = { +"41 50 3 1", +" c None", +". c #000000", +"+ c #FFFFFF", +" ", +" ", +" ", +" ", +" ", +" ", +" ......... ", +" ....+++++++.... ", +" ...+++++++++++++... ", +" ...++++++++++++++++. ", +" ..++++++++++++++++++. ", +" ..+++++++.........+++. ", +" ..++++++... ..... ", +" ..+++++... . ", +" ..+++++.. ", +" .++++++. ", +" ..+++++.. ", +" .++++++. ", +" ..+++++.. ", +" .++++++. ....... ", +" ..++++++. ...+++++.... ", +" .+++++++....++++++++++.. ", +" .++++++++++++++++++++++.. ", +" .+++++++++++++++++++++++.. ", +" ..++++++++++......++++++++. ", +" .+++++++++... ..+++++++.. ", +" .++++++++.. ..+++++++. ", +" .++++++++. .+++++++. ", +" .+++++++.. .+++++++. ", +" .+++++++. .+++++++. ", +" .+++++++. .+++++++. ", +" .+++++++. .++++++.. ", +" .+++++++. .++++++. ", +" ..++++++. ..++++++. ", +" .++++++. .++++++.. ", +" .++++++.. ..++++++. ", +" ..++++++.. ...++++++.. ", +" ..++++++......+++++++.. ", +" .++++++++++++++++++.. ", +" ..++++++++++++++++.. ", +" ...++++++++++++... ", +" ....++++++.... ", +" ........ ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/libimage/img7.xpm b/libimage/img7.xpm new file mode 100644 index 0000000..d9ef9e5 --- /dev/null +++ b/libimage/img7.xpm @@ -0,0 +1,56 @@ +/* XPM */ +static char * img7_xpm[] = { +"43 50 3 1", +" c None", +". c #000000", +"+ c #FFFFFF", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ........................... ", +" .++++++++++++++++++++++++.. ", +" .++++++++++++++++++++++++. ", +" .++++++++++++++++++++++++. ", +" ..++++++++++++++++++++++++. ", +" .++++++++++++++++++++++++.. ", +" .................+++++++.. ", +" .+++++++. ", +" ..++++++.. ", +" ..++++++.. ", +" .+++++++. ", +" ..++++++.. ", +" .++++++.. ", +" ..++++++. ", +" ..++++++.. ", +" .+++++++. ", +" ..++++++.. ", +" ..++++++.. ", +" .+++++++. ", +" ..++++++.. ", +" ..++++++.. ", +" .+++++++. ", +" ..++++++.. ", +" ..++++++.. ", +" .+++++++. ", +" ..++++++.. ", +" ..++++++.. ", +" .+++++++. ", +" ..++++++.. ", +" .++++++.. ", +" ..++++++. ", +" ..++++++.. ", +" .+++++++. ", +" ..++++++.. ", +" .......... ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/libimage/img8.xpm b/libimage/img8.xpm new file mode 100644 index 0000000..3baee10 --- /dev/null +++ b/libimage/img8.xpm @@ -0,0 +1,56 @@ +/* XPM */ +static char * img8_xpm[] = { +"42 50 3 1", +" c None", +". c #000000", +"+ c #FFFFFF", +" ", +" ", +" ", +" ", +" ", +" ", +" .......... ", +" .....++++++++.... ", +" ...+++++++++++++++.. ", +" ..++++++++++++++++++.. ", +" ..++++++++++++++++++++.. ", +" ..++++++++......++++++++.. ", +" .+++++++... ..++++++++. ", +" ..+++++++. ..+++++++. ", +" .+++++++.. .+++++++. ", +" .+++++++. .+++++++. ", +" .+++++++. .++++++.. ", +" .+++++++. ..++++++. ", +" ..++++++.. .++++++.. ", +" ..++++++.. ...+++++.. ", +" ..++++++......+++++... ", +" ...+++++++++++++... ", +" .+++++++++++++. ", +" ..+++++++++++++. ", +" ...++++++++++++++.. ", +" ...+++++......++++++.. ", +" ..+++++... ..++++++.. ", +" ..+++++.. ..++++++. ", +" ..++++++. .++++++.. ", +" .++++++.. .+++++++. ", +" .++++++. .+++++++. ", +" ..++++++. .+++++++. ", +" .+++++++. .+++++++. ", +" .+++++++. ..++++++.. ", +" .+++++++. .+++++++. ", +" .+++++++.. ..+++++++. ", +" ..+++++++.. ...+++++++.. ", +" .++++++++......++++++++.. ", +" ..++++++++++++++++++++.. ", +" ..++++++++++++++++++.. ", +" ...++++++++++++++... ", +" ....+++++++..... ", +" ......... ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/libimage/img9.xpm b/libimage/img9.xpm new file mode 100644 index 0000000..31debe9 --- /dev/null +++ b/libimage/img9.xpm @@ -0,0 +1,56 @@ +/* XPM */ +static char * img9_xpm[] = { +"39 50 3 1", +" c None", +". c #000000", +"+ c #FFFFFF", +" ", +" ", +" ", +" ", +" ", +" ", +" ........ ", +" ....++++++.... ", +" ...++++++++++++... ", +" ..++++++++++++++++.. ", +" ..++++++++++++++++++. ", +" ..+++++++......++++++.. ", +" ..++++++... ..++++++.. ", +" .++++++.. ..++++++. ", +" ..++++++. .++++++. ", +" .++++++.. .++++++.. ", +" .++++++. .+++++++. ", +" ..++++++. .+++++++. ", +" .+++++++. .+++++++. ", +" .+++++++. .+++++++. ", +" .+++++++. ..+++++++. ", +" .+++++++. .++++++++. ", +" .+++++++.. ..++++++++. ", +" ..+++++++.. ...+++++++++. ", +" .++++++++......++++++++++.. ", +" ..+++++++++++++++++++++++. ", +" ..++++++++++++++++++++++. ", +" ..++++++++++....+++++++. ", +" ....+++++... .++++++.. ", +" ....... .++++++. ", +" ..+++++.. ", +" .++++++. ", +" ..+++++.. ", +" .++++++. ", +" ..+++++.. ", +" . ...+++++.. ", +" .... ...++++++.. ", +" ..++.........+++++++.. ", +" .++++++++++++++++++.. ", +" .++++++++++++++++... ", +" ..+++++++++++++... ", +" ....+++++++.... ", +" ......... ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/libimage/pnm.c b/libimage/pnm.c new file mode 100644 index 0000000..3b59db9 --- /dev/null +++ b/libimage/pnm.c @@ -0,0 +1,512 @@ +/*************************************************************************** + * pnm.c + * (C) Copyright 2004 Andreas Brachold + * based on works from Fabian E. Bustamante + * Created: Thu Aug 7 2004 + ****************************************************************************/ + +/* + * 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 2 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +/* #include for size_t */ +#include +#include + +#include "pnm.h" + +/***************************************************************************** +Print error message and get out +@param variable list of arguments explaining error +@return - none*/ +void cPNM::error(char* format, ... ) +{ + if(m_szError) + free(m_szError); + m_szError = NULL; + + va_list args; + va_start( args, format ); + + (void) vasprintf( &m_szError, format, args ); + va_end( args ); +} + + +/***************************************************************************** +Get pnm magic number (P1, P2, ...) +@param file - source file +@return - magic number*/ +bool cPNM::MagicNumber(FILE* f,unsigned int& nFormat) +{ + int ich1, ich2; + + ich1 = getc( f ); + if ( ich1 == EOF ) { + cPNM::error( "End of file, read error reading magic number" ); + return false; + } + ich2 = getc( f ); + if ( ich2 == EOF ) { + cPNM::error( "End of file, read error reading magic number" ); + return false; + } + nFormat = ich1 * 256 + ich2; + return true; +} + +/***************************************************************************** +Get next significant character, i.e. jump over comments +@param file - source file +@param - Next significant character*/ +bool cPNM::getchar(FILE* f, char& nNext) +{ + register int ich; + register char ch; + + ich = getc( f ); + if ( ich == EOF ) + { + cPNM::error( "End of file, read error" ); + return false; + } + ch = (char) ich; + + if ( ch == '#' ) { + do { + ich = getc( f ); + if ( ich == EOF ) + { + cPNM::error( "End of file, read error" ); + return false; + } + ch = (char) ich; + } while ( ch != '\n' && ch != '\r' ); + } + + nNext = ch; + return true; +} + + +/***************************************************************************** +Get next bit from file +@param file - source file +@param - Next bit*/ +bool cPNM::getbit(FILE* f, bit& nNext) +{ + register char ch; + + do { + if(!getchar( f, ch )) + return false; + } while ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' ); + + if ( ch != '0' && ch != '1' ) + { + cPNM::error( "junk in file where bits should be" ); + return false; + } + nNext = ( ch == '1' ) ? 1 : 0; + return true; +} + + +/***************************************************************************** +Get next byte from file +@param file - source file +@param - Next byte */ +bool cPNM::getrawbyte(FILE* f,unsigned char& nNext ) +{ + register int iby; + + iby = getc( f ); + if ( iby == EOF ) + { + cPNM::error( "End of file, read error" ); + return false; + } + nNext = (unsigned char) iby; + return true; +} + + +/***************************************************************************** +Get integer from file +@param file - source file +@param - Next integer */ +//bool cPNM::getint(FILE* f, unsigned int& nNext) +template bool cPNM::getint(FILE* f, T& nNext) +{ + register char ch; + register T i; + + do { + if(!getchar( f,ch )) + return false; + } while ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' ); + + if ( ch < '0' || ch > '9' ) + { + cPNM::error( "junk in file where an integer should be" ); + return false; + } + i = 0; + do { + i = i * 10 + ch - '0'; + if(!getchar( f,ch )) + return false; + } while ( ch >= '0' && ch <= '9' ); + nNext = i; + return true; +} + + +/***************************************************************************** +Read Header from file +@param FILE* f - source file +@return bool - true successful reading*/ +bool cPNM::readHeader(FILE* f) +{ + if(!MagicNumber(f,m_nFormat) /* Read magic number. */ + || !getint(f,m_nWidth) /* Read size. */ + || !getint(f,m_nHeight)) + return false; + + /* Check magic number. */ + switch (PNM_FORMAT_TYPE(m_nFormat)) { + case PPM_TYPE: + /* Read m_nColorDepth. */ + if(!getint(f,m_nColorDepth)) + return false; + if ( (m_nColorDepth) > PPM_MAXMAXVAL ) + { + cPNM::error( "ColorDepth is too large" ); + return false; + } + return true; + + case PGM_TYPE: + /* Read ColorDepth. */ + if(!getint(f,m_nColorDepth)) + return false; + if ( m_nColorDepth > PGM_MAXMAXVAL ) + { + cPNM::error( "ColorDepth is too large" ); + return false; + } + return true; + + case PBM_TYPE: + m_nColorDepth = 1; /* pbmmaxval */ + return true; + + default: + cPNM::error( "bad magic number - not a ppm, pgm, or pbm file" ); + } + + return false; + +} + + +/***************************************************************************** +Read Row from file with Black White Values +@param FILE* f - source file +@return bool - true successful reading*/ +bool cPNM::readpbmrow(FILE* f, bit* pBits) +{ + register unsigned int w; + register int bitshift; + register unsigned char item; + register bit* bP; + + switch ( m_nFormat ) { + case PBM_FORMAT: + for ( w = 0, bP = pBits; w < m_nWidth; ++w, ++bP ) + { + if(!getbit( f,*bP )) + return false; + } + return true; + + case RPBM_FORMAT: + bitshift = -1; + item = 0; + for ( w = 0, bP = pBits; w < m_nWidth; ++w, ++bP ) { + if ( bitshift == -1 ) { + if(!getrawbyte( f,item )) + return false; + bitshift = 7; + } + *bP = ( item >> bitshift ) & 1; + --bitshift; + } + return true; + + default: + cPNM::error( "Wrong fileformat" ); + return false; + } +} /* end cPNM::readpbmrow() */ + + +/***************************************************************************** +Read Row from file with Gray Values +@param FILE* f - source file +@return bool - true successful reading*/ +bool cPNM::readpgmrow( FILE* f, gray* pGrays) +{ + register unsigned int w; + register gray* gP; + bit* pBits; + register bit* bP; + + switch ( m_nFormat ) { + case PGM_FORMAT: + for ( w = 0, gP = pGrays; w < m_nWidth; ++w, ++gP ) { + if(!getint( f,*gP )) + return false; + } + return true; + + case RPGM_FORMAT: + for ( w = 0, gP = pGrays; w < m_nWidth; ++w, ++gP ) { + if(!getrawbyte( f,*gP )) + return false; + } + return true; + + case PBM_FORMAT: + case RPBM_FORMAT: + if(!allocrow( &pBits ) + || !readpbmrow( f, pBits )) + { + freerow( pBits ); + return false; + } + for ( w = 0, gP = pGrays, bP = pBits; w < m_nWidth; ++w, ++gP, ++bP ) + *gP = ( *bP == PBM_WHITE ) ? m_nColorDepth : 0; + freerow( pBits ); + return true; + + default: + cPNM::error( "Wrong fileformat" ); + return false; + } +} + + + +/***************************************************************************** +Read Row from file with Colored Values +@param FILE* f - source file +@return bool - true successful reading*/ +bool cPNM::readppmrow(FILE* f, pixel* pixelrow) +{ + register unsigned int w; + register pixel* pP; + register pixval r, g, b; + gray* pGrays; + register gray* gP; + bit* pBits; + register bit* bP; + + switch ( m_nFormat ) { + case PPM_FORMAT: + for ( w = 0, pP = pixelrow; w < m_nWidth; ++w, ++pP ) { + if(!getint( f,r ) + ||!getint( f,g ) + ||!getint( f,b )) + return false; + PPM_ASSIGN( *pP, r, g, b ); + } + return true; + + case RPPM_FORMAT: + for ( w = 0, pP = pixelrow; w < m_nWidth; ++w, ++pP ) { + if(!getrawbyte( f,r ) + ||!getrawbyte( f,g ) + ||!getrawbyte( f,b )) + return false; + PPM_ASSIGN( *pP, r, g, b ); + } + return true; + + case PGM_FORMAT: + case RPGM_FORMAT: + if(!allocrow(&pGrays) + || !readpgmrow( f, pGrays )) + { + freerow( pGrays ); + return false; + } + for ( w = 0, gP = pGrays, pP = pixelrow; w < m_nWidth; ++w, ++gP, ++pP ) { + r = *gP; + PPM_ASSIGN( *pP, r, r, r ); + } + freerow( pGrays ); + return true; + + case PBM_FORMAT: + case RPBM_FORMAT: + if(!allocrow( &pBits ) + || !readpbmrow( f, pBits )) + { + freerow( pBits ); + return false; + } + for ( w = 0, bP = pBits, pP = pixelrow; w < m_nWidth; ++w, ++bP, ++pP ) { + r = ( *bP == PBM_WHITE ) ? m_nColorDepth : 0; + PPM_ASSIGN( *pP, r, r, r ); + } + freerow( pBits ); + return true; + + default: + cPNM::error( "Wrong fileformat" ); + + return false; + } +} + + +/***************************************************************************** +Read Row from file with any Values +@param FILE* f - source file +@return bool - true successful reading*/ +bool cPNM::readrow(FILE* f, xel* xelrow) +{ + register unsigned int w; + register xel* xP; + gray* pGrays; + register gray* gP; + bit* pBits; + register bit* bP; + + switch ( PNM_FORMAT_TYPE(m_nFormat) ) { + case PPM_TYPE: + return readppmrow( f, (pixel*) xelrow ); + + case PGM_TYPE: + if(!allocrow(&pGrays) + || !readpgmrow( f, pGrays )) + { + freerow( pGrays ); + return false; + } + for ( w = 0, xP = xelrow, gP = pGrays; w < m_nWidth; ++w, ++xP, ++gP ) { + PNM_ASSIGN1( *xP, *gP ); + } + freerow( pGrays ); + return true; + + case PBM_TYPE: + if(!allocrow(&pBits) + || !readpbmrow( f, pBits )) + { + freerow( pBits ); + return false; + } + for ( w = 0, xP = xelrow, bP = pBits; w < m_nWidth; ++w, ++xP, ++bP ) { + PNM_ASSIGN1( *xP, *bP == PBM_BLACK ? 0: PNM_MAXMAXVAL ); + } + freerow( pBits ); + return true; + + default: + cPNM::error( "Wrong fileformat" ); + return false; + } +} + + + + +cPNM::cPNM() +{ + m_nFormat = 0; + m_nColorDepth = 0; + m_nHeight = 0; + m_nWidth = 0; + m_szError = NULL; +} + +cPNM::~cPNM() +{ + if(m_szError) + free(m_szError); +} + + + + +/* +int main() +{ + cPNM pnmImage; + FILE *f=fopen("test.pnm", "r"); + if(f) + { + xel* pRow = NULL; + register unsigned int w; + register unsigned int h; + + if(pnmImage.readHeader(f)) + { + for(h = 0;h < pnmImage.GetHeight() && h < GetHeight();++h) + { + if(!pnmImage.allocrow(&pRow) + ||!pnmImage.readrow(f, pRow) ) + break; + + xel* pP = pRow; + if(pnmImage.GetWidth() < GetWidth()) + w = (GetWidth() - pnmImage.GetWidth()) / 2; + else w = 0; + for(;w < pnmImage.GetWidth() && w < GetWidth();++w,++pP) + { + unsigned char* pImageRGB = m_pImageRGB + (((h*GetWidth())+w)*3); + if(pnmImage.GetColorDepth() == 255) + { + *(pImageRGB + 0) = (unsigned char) PPM_GETR(*pP); + *(pImageRGB + 1) = (unsigned char) PPM_GETG(*pP); + *(pImageRGB + 2) = (unsigned char) PPM_GETB(*pP); + } + else if(pnmImage.GetColorDepth() == 1) + { + *(pImageRGB + 0) = (unsigned char) PPM_GETR(*pP)==0?0x00:0xFF; + *(pImageRGB + 1) = (unsigned char) PPM_GETG(*pP)==0?0x00:0xFF; + *(pImageRGB + 2) = (unsigned char) PPM_GETB(*pP)==0?0x00:0xFF; + } + else + { + *(pImageRGB + 0) = (unsigned char) (PPM_GETR(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF; + *(pImageRGB + 1) = (unsigned char) (PPM_GETG(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF; + *(pImageRGB + 2) = (unsigned char) (PPM_GETB(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF; + } + } + pnmImage.freerow(pRow); + pRow = NULL; + } + if(pRow) + pnmImage.freerow((char*)pRow); + } + fclose(f); + } +} +*/ diff --git a/libimage/pnm.h b/libimage/pnm.h new file mode 100644 index 0000000..f28e829 --- /dev/null +++ b/libimage/pnm.h @@ -0,0 +1,218 @@ +/*************************************************************************** + * pnm.h + * (C) Copyright 2004 Andreas Brachold + * based on works from Fabian E. Bustamante + * Created: Thu Aug 7 2004 + ****************************************************************************/ + +/* + * 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 2 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef _pnm_h +#define _pnm_h + +/* + * CONFIGURE: PGM can store gray values as either bytes or shorts. For most + * applications, bytes will be big enough, and the memory savings can be + * substantial. However, if you need more than 8 bits of grayscale resolution, + * then define this symbol. + */ +//#define PGM_BIGGRAYS + +/* + * CONFIGURE: Normally, PPM handles a pixel as a struct of three grays. + * If grays are stored in bytes, that's 24 bits per color pixel; if + * grays are stored as shorts, that's 48 bits per color pixel. PPM + * can also be configured to pack the three grays into a single longword, + * 10 bits each, 30 bits per pixel. + * + * If you have configured PGM with the PGM_BIGGRAYS option, AND you don't + * need more than 10 bits for each color component, AND you care more about + * memory use than speed, then this option might be a win. Under these + * circumstances it will make some of the programs use 1.5 times less space, + * but all of the programs will run about 1.4 times slower. + * + * If you are not using PGM_BIGGRAYS, then this option is useless -- it + * doesn't save any space, but it still slows things down. + */ +/* #define PPM_PACKCOLORS */ + +/* END CONFIGURE */ + + +typedef unsigned char bit; +const bit PBM_WHITE = 0; +const bit PBM_BLACK = 1; + +/* Magic constants. P1,P2,P3...P6 */ + +const unsigned char PBM_MAGIC1 = 'P'; +const unsigned char PBM_MAGIC2 = '1'; +const unsigned char RPBM_MAGIC2 = '4'; +const unsigned short PBM_FORMAT = (PBM_MAGIC1 * 256 + PBM_MAGIC2); +const unsigned short RPBM_FORMAT = (PBM_MAGIC1 * 256 + RPBM_MAGIC2); +#define PBM_TYPE PBM_FORMAT + +const unsigned char PGM_MAGIC1 = 'P'; +const unsigned char PGM_MAGIC2 = '2'; +const unsigned char RPGM_MAGIC2 = '5'; +const unsigned short PGM_FORMAT = (PGM_MAGIC1 * 256 + PGM_MAGIC2); +const unsigned short RPGM_FORMAT = (PGM_MAGIC1 * 256 + RPGM_MAGIC2); +#define PGM_TYPE PGM_FORMAT + +const unsigned char PPM_MAGIC1 = 'P'; +const unsigned char PPM_MAGIC2 = '3'; +const unsigned char RPPM_MAGIC2 = '6'; +const unsigned short PPM_FORMAT = (PPM_MAGIC1 * 256 + PPM_MAGIC2); +const unsigned short RPPM_FORMAT = (PPM_MAGIC1 * 256 + RPPM_MAGIC2); +#define PPM_TYPE PPM_FORMAT + +#ifdef PGM_BIGGRAYS +typedef unsigned short gray; +#define PGM_MAXMAXVAL 65535 +#else /*PGM_BIGGRAYS*/ +typedef unsigned char gray; +#define PGM_MAXMAXVAL 255 +#endif /*PGM_BIGGRAYS*/ + +typedef gray pixval; + +#ifdef PPM_PACKCOLORS + +#define PPM_MAXMAXVAL 1023 +typedef unsigned long pixel; +#define PPM_GETR(p) (((p) & 0x3ff00000) >> 20) +#define PPM_GETG(p) (((p) & 0xffc00) >> 10) +#define PPM_GETB(p) ((p) & 0x3ff) +#define PPM_ASSIGN(p,red,grn,blu) (p) = ((pixel) (red) << 20) | ((pixel) (grn) << 10) | (pixel) (blu) + +#else /*PPM_PACKCOLORS*/ + +#define PPM_MAXMAXVAL PGM_MAXMAXVAL +typedef struct +{ + pixval r, g, b; +} pixel; + +#define PPM_GETR(p) ((p).r) +#define PPM_GETG(p) ((p).g) +#define PPM_GETB(p) ((p).b) +#define PPM_ASSIGN(p,red,grn,blu) do { (p).r = (red); (p).g = (grn); (p).b = (blu); } while ( 0 ) + +#endif /*PPM_PACKCOLORS*/ + +typedef pixel xel; +typedef pixval xelval; +#define PNM_MAXMAXVAL PPM_MAXMAXVAL +#define PNM_ASSIGN1(x,v) PPM_ASSIGN(x,v,v,v) + + +/* Macro for turning a format number into a type number. */ +#define PBM_FORMAT_TYPE(f) ((f) == PBM_FORMAT || (f) == RPBM_FORMAT ? PBM_TYPE : -1) +#define PGM_FORMAT_TYPE(f) ((f) == PGM_FORMAT || (f) == RPGM_FORMAT ? PGM_TYPE : PBM_FORMAT_TYPE(f)) +#define PNM_FORMAT_TYPE(f) ((f) == PPM_FORMAT || (f) == RPPM_FORMAT ? PPM_TYPE : PGM_FORMAT_TYPE(f)) + + +class cPNM +{ + unsigned int m_nFormat; + unsigned int m_nColorDepth; + unsigned int m_nHeight; + unsigned int m_nWidth; + char* m_szError; +protected: + /***************************************************************************** + Print error message and get out + @param variable list of arguments explaining error + @return - none*/ + void error (char* format, ... ); + + /***************************************************************************** + Get pnm magic number (P1, P2, ...) + @param file - source file + @return - magic number*/ + bool MagicNumber(FILE* f,unsigned int& nFormat); + + /***************************************************************************** + Get next significant character, i.e. jump over comments + @param file - source file + @param - Next significant character*/ + bool getchar(FILE* f,char& nNext); + + /***************************************************************************** + Get next bit from file + @param file - source file + @param - Next bit*/ + bool getbit(FILE* f, bit& nNext ); + + /***************************************************************************** + Get next byte from file + @param file - source file + @param - Next byte */ + bool getrawbyte(FILE* f, unsigned char& nNext); + + /***************************************************************************** + Get integer from file + @param file - source file + @param - Next integer */ + //bool getint(FILE* file, unsigned int& nNext); + template bool getint(FILE* f, T& nNext); + + + bool readpbmrow(FILE* file, bit* bitrow); + bool readpgmrow(FILE* file, gray* grayrow); + bool readppmrow(FILE* file, pixel* pixelrow); + +public: + cPNM(); + virtual ~cPNM(); + + + bool readHeader(FILE* Infp); + bool readrow(FILE* file, xel* xelrow); + inline unsigned int GetHeight() const { return m_nHeight;} + inline unsigned int GetWidth() const { return m_nWidth;} + inline unsigned int GetColorDepth() const { return m_nColorDepth;} + /***************************************************************************** + Get an array from memory to hold 'm_nWidth' elements of size 'size' + @param int size - of each element + @return char* - Pointer to allocated array */ + template bool allocrow(T** p) + { + *p = (T*) malloc( m_nWidth * sizeof(T) ); + if (*p == (T*) 0 ) + { + cPNM::error( "out of memory allocating a row" ); + return false; + } + return true; + } + /***************************************************************************** + Free memory chunk previously assigned + @param char *p - pointer to memory chunk + @return - none */ + template void freerow(T *p) + { + if(p != NULL) + free((char*)p); + } + + const char* GetError() const { return m_szError; } +}; + + +#endif /* _pnm_h */ diff --git a/libimage/xpm.c b/libimage/xpm.c new file mode 100644 index 0000000..9572ec4 --- /dev/null +++ b/libimage/xpm.c @@ -0,0 +1,224 @@ +/*************************************************************************** + * xpm.c + * + * (C) Copyright 2004 Andreas Brachold + * Created: Thu Aug 11 2004 + * + ****************************************************************************/ + +/* + * 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 2 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include + + +#include "img1.xpm" +#include "img2.xpm" +#include "img3.xpm" +#include "img4.xpm" +#include "img5.xpm" +#include "img6.xpm" +#include "img7.xpm" +#include "img8.xpm" +#include "img9.xpm" + +#include "error.xpm" +#include "error_small.xpm" + +#include "xpm.h" + +cXPM gOverlay_Image_1(img1_xpm); +cXPM gOverlay_Image_2(img2_xpm); +cXPM gOverlay_Image_3(img3_xpm); +cXPM gOverlay_Image_4(img4_xpm); +cXPM gOverlay_Image_5(img5_xpm); +cXPM gOverlay_Image_6(img6_xpm); +cXPM gOverlay_Image_7(img7_xpm); +cXPM gOverlay_Image_8(img8_xpm); +cXPM gOverlay_Image_9(img9_xpm); + +cXPM gOverlay_Error(error_xpm); +cXPM gOverlay_ErrorSmall(error_small_xpm); + +cXPM::cXPM(char* pXPM[]) +:m_Colors(NULL) +{ + char **p = pXPM; + int c; + if (4 != sscanf(*p, "%d %d %d %d", &m_nWidth, &m_nHeight, &m_nColors, &c) + || c != 1 ) + { + esyslog("imageplugin: ERROR! faulty 'values' line in XPM: '%s'", *p); + return; + } + + m_Colors = new colormap[m_nColors]; + + for (unsigned int n = 0; n < m_nColors; ++n) { + bool bColorNone = false; + const char *s = *++p; + if (int(strlen(s)) < c) { + esyslog("imageplugin: ERROR! faulty 'colors' line in XPM: '%s'", s); + return; + } + (m_Colors+n)->color = *s; + s = skipspace(s + c); + if (*s != 'c') { + esyslog("imageplugin: ERROR! unknown color key in XPM: '%c'", *s); + return; + } + s = skipspace(s + 1); + if (strcasecmp(s, "none") == 0) { + s = "#000000"; + bColorNone = true; + } + if (*s != '#') { + esyslog("imageplugin: ERROR! unknown color code in XPM: '%c'", *s); + return; + } + (m_Colors+n)->rgb = strtoul(++s, NULL, 16) | (bColorNone?0xFF000000:0x00000000); + } + + m_pXPM = p; + for (unsigned int l = 0; l < m_nHeight; l++) { + const char *s = *++p; + if (strlen(s) != m_nWidth * c) { + esyslog("imageplugin: ERROR! faulty pixel line in XPM: %d '%s'", l, s); + m_pXPM = NULL; + return; + } + } +} + +cXPM::~cXPM() +{ + if(m_Colors) + delete[] m_Colors; +} + + +bool cXPM::Overlay(unsigned char* pRGBMem,unsigned int nMemWidth,unsigned int nMemHeight,unsigned int OffLeft,unsigned int OffTop) const +{ + if(!m_Colors || !pRGBMem || !m_pXPM) + return false; + + char **p = m_pXPM; + + for(unsigned int h = 0; + h < m_nHeight + && h+OffTop < nMemHeight; + ++h) + { + const char *s = *++p; + for(unsigned int w = 0;w < m_nWidth + && w+OffLeft < nMemWidth; + ++w,++s) + { + unsigned int rgb; + if(GetColor(*s,rgb)) + { + unsigned char* pImageRGB = pRGBMem + + ((((h+OffTop)*nMemWidth)+w+OffLeft)*3); + + *(pImageRGB + 0) = (unsigned char)(rgb >> 16) & 0xFF; + *(pImageRGB + 1) = (unsigned char)(rgb >> 8 ) & 0xFF; + *(pImageRGB + 2) = (unsigned char)(rgb ) & 0xFF; + } + } + } + + return true; +} + + + +bool cXPM::GetColor(unsigned int color,unsigned int& rgb) const +{ + unsigned int n = 0; + for(;ncolor == color) + { + rgb = (m_Colors+n)->rgb; + return (0 == (rgb & 0xFF000000)); // Check for color "none" + } + } + return false; +} + +bool cXPM::Overlay(const char sz,unsigned char* pRGBMem, + unsigned int nMemWidth,unsigned int nMemHeight, + cXPM::ePlacement place, unsigned int nOffLeft,unsigned int nOffTop, + unsigned int nWidth,unsigned int nHeight) +{ + cXPM* p = NULL; + switch(sz) + { + case '1': p=&gOverlay_Image_1;break; + case '2': p=&gOverlay_Image_2;break; + case '3': p=&gOverlay_Image_3;break; + case '4': p=&gOverlay_Image_4;break; + case '5': p=&gOverlay_Image_5;break; + case '6': p=&gOverlay_Image_6;break; + case '7': p=&gOverlay_Image_7;break; + case '8': p=&gOverlay_Image_8;break; + case '9': p=&gOverlay_Image_9;break; + case 's': p=&gOverlay_ErrorSmall;break; + } + if(p) + { + switch(place) + { + case TopLeft: + return p->Overlay(pRGBMem, + nMemWidth,nMemHeight, + nOffLeft, + nOffTop); + case TopRight: + return p->Overlay(pRGBMem, + nMemWidth,nMemHeight, + nOffLeft+nWidth-p->m_nWidth-16, + nOffTop); + case Center: + return p->Overlay(pRGBMem, + nMemWidth,nMemHeight, + nOffLeft + nWidth/2 - p->m_nWidth/2, + nOffTop + nHeight/2 - p->m_nHeight/2); + case BottomLeft: + return p->Overlay(pRGBMem, + nMemWidth,nMemHeight, + nOffLeft, + nOffTop+nHeight-p->m_nHeight); + case BottomRight: + return p->Overlay(pRGBMem, + nMemWidth,nMemHeight, + nOffLeft+nWidth-p->m_nWidth-16, + nOffTop+nHeight-p->m_nHeight); + } + } + + return false; +} + +bool cXPM::Error(unsigned char* pRGBMem,unsigned int nMemWidth,unsigned int nMemHeight) +{ + return gOverlay_Error.Overlay(pRGBMem, + nMemWidth,nMemHeight, + (nMemWidth - gOverlay_Error.m_nWidth)/2, + (nMemHeight - gOverlay_Error.m_nHeight)/2); +} diff --git a/libimage/xpm.h b/libimage/xpm.h new file mode 100644 index 0000000..3e9fbb5 --- /dev/null +++ b/libimage/xpm.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * xpm.h + * + * (C) Copyright 2004 Andreas Brachold + * Created: Thu Aug 11 2004 + * + ****************************************************************************/ + +/* + * 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 2 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef _xpm_h +#define _xpm_h + +class cXPM +{ + + char **m_pXPM; + unsigned int m_nWidth; + unsigned int m_nHeight; + unsigned int m_nColors; + + struct colormap + { + unsigned int color; + unsigned int rgb; + } *m_Colors; +protected: + bool GetColor(unsigned int color,unsigned int& rgb) const; +public: + cXPM(char* pXPM[]); + virtual ~cXPM(); + + enum ePlacement + { + TopLeft, + TopRight, + Center, + BottomLeft, + BottomRight + }; + + bool Overlay(unsigned char* pRGBMem,unsigned int nMemWidth,unsigned int nMemHeight,unsigned int OffLeft,unsigned int OffTop) const; + static bool Overlay(const char sz,unsigned char* pRGBMem, + unsigned int nMemWidth,unsigned int nMemHeight, + cXPM::ePlacement place, unsigned int nOffLeft,unsigned int nOffTop, + unsigned int nWidth,unsigned int nHeight); + static bool Error(unsigned char* pRGBMem,unsigned int nMemWidth,unsigned int nMemHeight); +}; + +#endif /* _xpm_h */ diff --git a/liboutput/Makefile b/liboutput/Makefile new file mode 100644 index 0000000..acac360 --- /dev/null +++ b/liboutput/Makefile @@ -0,0 +1,58 @@ +# +# Makefile for a Video Disk Recorder plugin +# +# $Id$ + +VDRDIR = ../../../.. +DVBDIR = ../../../../../DVB +FFMDIR = ../../../../../ffmpeg + +### The C++ compiler and options: + +CXX ?= g++ +CXXFLAGS ?= -O0 -g -Wall -Woverloaded-virtual + +-include $(VDRDIR)/Make.config + +### The directory environment: + + +INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include + +DEFINES += -D_GNU_SOURCE + +LIBS += + +ifdef FFMPEG_STATIC + INCLUDES += -I$(FFMDIR) + DEFINES += -DHAVE_FFMPEG_STATIC +endif + +### The object files (add further files here): + +OBJS = encode.o stillimage.o stillimage-player.o + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< + +# Dependencies: + +MAKEDEP = $(CXX) -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Targets: + +all: liboutput.a + +liboutput.a : $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) +# $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@ + +clean: + @-rm -f $(OBJS) $(DEPFILE) *.a *.so *.tgz core* *~ diff --git a/liboutput/encode.c b/liboutput/encode.c new file mode 100644 index 0000000..351e30f --- /dev/null +++ b/liboutput/encode.c @@ -0,0 +1,369 @@ +/*************************************************************************** + * encode.c + * + * (C) Copyright 2004 Andreas Brachold + * Created: Thu Aug 5 2004 + * + ****************************************************************************/ + +/* + * 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 2 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#ifndef HAVE_FFMPEG_STATIC +#include +#endif + +#include "encode.h" +#include +#include + +typedef void (*f_avcodec_init)(void); +typedef void (*f_avcodec_register_all)(void); +typedef AVCodec *(*f_avcodec_find_encoder)(enum CodecID id); +typedef AVCodecContext *(*f_avcodec_alloc_context)(void); +typedef AVFrame *(*f_avcodec_alloc_frame)(void); +typedef int (*f_avcodec_open)(AVCodecContext *avctx, AVCodec *codec); +typedef int (*f_avcodec_close)(AVCodecContext *avctx); +typedef int (*f_avcodec_encode_video)(AVCodecContext *avctx, uint8_t *buf, int buf_size,const AVFrame *pict); +typedef int (*f_avpicture_fill)(AVPicture *picture, uint8_t *ptr, int pix_fmt, int width, int height); + +#if FFMPEG_VERSION_INT <= 0x000408 +typedef int (*f_img_convert)(AVPicture *dst, int dst_pix_fmt,AVPicture *src, int pix_fmt,int width, int height); +#elif FFMPEG_VERSION_INT >= 0x000409 +typedef int (*f_img_convert)(AVPicture *dst, int dst_pix_fmt,const AVPicture *src, int pix_fmt,int width, int height); +#else + #error "there is a unknow ffmpeg version or FFMPEG_VERSION_INT isnt defined" +#endif + +#ifdef HAVE_FFMPEG_STATIC + +static f_avcodec_init fn_avcodec_init=avcodec_init; +static f_avcodec_register_all fn_avcodec_register_all=avcodec_register_all; +static f_avcodec_find_encoder fn_avcodec_find_encoder=avcodec_find_encoder; +static f_avcodec_alloc_context fn_avcodec_alloc_context=avcodec_alloc_context; +static f_avcodec_alloc_frame fn_avcodec_alloc_frame=avcodec_alloc_frame; +static f_avcodec_open fn_avcodec_open=avcodec_open; +static f_avcodec_close fn_avcodec_close=avcodec_close; +static f_avcodec_encode_video fn_avcodec_encode_video=&avcodec_encode_video; +static f_avpicture_fill fn_avpicture_fill=avpicture_fill; +static f_img_convert fn_img_convert=img_convert; + +#else + +static f_avcodec_init fn_avcodec_init; +static f_avcodec_register_all fn_avcodec_register_all; +static f_avcodec_find_encoder fn_avcodec_find_encoder; +static f_avcodec_alloc_context fn_avcodec_alloc_context; +static f_avcodec_alloc_frame fn_avcodec_alloc_frame; +static f_avcodec_open fn_avcodec_open; +static f_avcodec_close fn_avcodec_close; +static f_avcodec_encode_video fn_avcodec_encode_video; +static f_avpicture_fill fn_avpicture_fill; +static f_img_convert fn_img_convert; + + +#define DLSYM(cast,sym,func) \ + sym=(cast)dlsym(m_hLibAvcodec, func); \ + bSuccess &= (sym != NULL); \ + if(sym == NULL) \ + esyslog("imageplugin: Link to function %s failed", func); + +void *cEncode::m_hLibAvcodec = NULL; + + +/******************************************************************************* + +*/ +bool cEncode::InitLibAVCodec(void){ + + bool bSuccess=false; + // Let ld.so search the libary + const char* szLibary = "libavcodec.so"; + m_hLibAvcodec=dlopen(szLibary, RTLD_LAZY); + if(!m_hLibAvcodec) + { + esyslog("imageplugin: Loading %s failed : %s", szLibary,dlerror()); + } + /* map the lib's syms to our wrapper */ + else { + + bSuccess=true; + DLSYM(f_avcodec_init,fn_avcodec_init,"avcodec_init"); + DLSYM(f_avcodec_init,fn_avcodec_register_all,"avcodec_register_all"); + DLSYM(f_avcodec_find_encoder,fn_avcodec_find_encoder,"avcodec_find_encoder"); + DLSYM(f_avcodec_alloc_context,fn_avcodec_alloc_context,"avcodec_alloc_context"); + DLSYM(f_avcodec_alloc_frame,fn_avcodec_alloc_frame,"avcodec_alloc_frame"); + DLSYM(f_avcodec_open,fn_avcodec_open,"avcodec_open"); + DLSYM(f_avcodec_close,fn_avcodec_close,"avcodec_close"); + DLSYM(f_avcodec_encode_video,fn_avcodec_encode_video,"avcodec_encode_video"); + DLSYM(f_avpicture_fill,fn_avpicture_fill,"avpicture_fill"); + DLSYM(f_img_convert,fn_img_convert,"img_convert"); + } + + return(bSuccess); +} + +/******************************************************************************* + +*/ +void cEncode::CloseLibAVCodec(void){ + + if (m_hLibAvcodec) + dlclose(m_hLibAvcodec); + m_hLibAvcodec = NULL; +} +#endif + +/******************************************************************************* + +*/ +cEncode::cEncode() +: m_pavCodec(NULL) +, m_pImageFilled(NULL) +, m_pImageYUV(NULL) +, m_nFrames(4) +, m_nData(0) +, m_pMPEG(NULL) +, m_pImageRGB(NULL) +{ + m_bLoaded = false; + m_pFramesSize = new unsigned int[m_nFrames]; + + m_bUsePAL = (cDevice::PrimaryDevice()->GetVideoSystem() == vsPAL); + m_nWidth = 720; + m_nHeight = m_bUsePAL ? 576 : 480; + + + m_nMaxMPEGSize = m_nWidth*m_nHeight * 3; //It see for me 500kb are enough, therefore should the double really enough memory + + /* Allocate bufers */ + if(NULL == (m_pMPEG=(uint8_t *)malloc(m_nMaxMPEGSize*3)) //~1200kb + || NULL == (m_pImageRGB=(uint8_t *)malloc(m_nWidth*m_nHeight*3)) //~1200kb + || NULL == (m_pImageFilled=(uint8_t *)malloc(m_nWidth*m_nHeight*3)) //~1200kb + || NULL == (m_pImageYUV=(uint8_t *)malloc(m_nWidth*m_nHeight*3/2))) //~600kb + { + esyslog("imageplugin: Failed to alloc memory for bitmaps."); + return; + } + + m_pavCodec = fn_avcodec_find_encoder(CODEC_ID_MPEG2VIDEO); + if (!m_pavCodec) { + esyslog("imageplugin: Failed to find CODEC_ID_MPEG2VIDEO."); + return; + } + m_bLoaded = true; +} + +bool cEncode::Register() +{ + #ifndef HAVE_FFMPEG_STATIC + if(!InitLibAVCodec()) { + esyslog("imageplugin: Failed to InitLibAVCodec."); + return false; + } + #endif + fn_avcodec_init(); + fn_avcodec_register_all(); + return true; +} + +void cEncode::UnRegister() +{ +#ifndef HAVE_FFMPEG_STATIC + CloseLibAVCodec(); +#endif +} + +void cEncode::ClearRGBMem() +{ + if(m_pImageRGB && m_bLoaded) + memset(m_pImageRGB,0,m_nWidth*m_nHeight*3 ); +} + +/******************************************************************************* + +*/ +cEncode::~cEncode(void) +{ + if(m_pImageYUV) + free(m_pImageYUV); + + if(m_pImageFilled) + free(m_pImageFilled); + + if(m_pImageRGB) + free(m_pImageRGB); + + if(m_pMPEG) + free(m_pMPEG); + delete m_pFramesSize; +} + +/******************************************************************************* + +*/ +bool cEncode::Encode() +{ + m_nData = 0; + + if (!m_bLoaded){ + dsyslog("imageplugin: libavcodec is'nt successful loaded."); + return false; + } + + bool bSuccess = false; + unsigned int i; + AVCodecContext *pAVCC = NULL; + AVFrame *pAVF = NULL; + int nSize=m_nWidth*m_nHeight; + + if(NULL == (pAVCC = fn_avcodec_alloc_context()) + || NULL == (pAVF = fn_avcodec_alloc_frame())) { + esyslog("imageplugin: Failed to alloc memory for AVCODEC and CONTEXT."); + goto encexit; + } + + pAVCC->bit_rate=1000000; //1000kbit + pAVCC->width = m_nWidth; + pAVCC->height = m_nHeight; + pAVCC->frame_rate=GetFrameRate(); + pAVCC->frame_rate_base=1; + pAVCC->gop_size=m_nFrames-1; //IPB //1 => Encode only I-Frames, bigger + pAVCC->max_b_frames=1; + pAVCC->flags |= CODEC_FLAG_QSCALE; + + if (fn_avcodec_open(pAVCC, m_pavCodec)<0) { + esyslog("imageplugin: Coldn't open Codec."); + goto encexit; + } + + pAVF->data[0]=m_pImageYUV; + pAVF->data[1]=m_pImageYUV+nSize; + pAVF->data[2]=pAVF->data[1]+nSize/4; + pAVF->linesize[0]=m_nWidth; + pAVF->linesize[1]=pAVF->linesize[2]=m_nWidth/2; + pAVF->quality = 1; + + // Convert RGB to YUV + if(!fn_avpicture_fill((AVPicture*)m_pImageFilled, m_pImageRGB, PIX_FMT_RGB24, + m_nWidth, m_nHeight)) { + esyslog("imageplugin: failed avpicture_fill"); + goto encexit; + } + + if(fn_img_convert((AVPicture*)pAVF->data, PIX_FMT_YUV420P, + (AVPicture*)m_pImageFilled, PIX_FMT_RGB24, m_nWidth, m_nHeight)) { + esyslog("imageplugin: failed convert RGB to YUV"); + goto encexit; + } + + // Encode Frames which defined with m_nFrames + for(i=0; i + * Created: Thu Aug 5 2004 + * + ****************************************************************************/ + +/* + * 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 2 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _ENCODE_H +#define _ENCODE_H + +extern "C" +{ +#ifdef HAVE_FFMPEG_STATIC +# include +#else +# include +//#include +#endif +} + +//#define TESTCODE + +class cEncode +{ + AVCodec *m_pavCodec; + unsigned int m_nMaxMPEGSize; + uint8_t *m_pImageFilled; + uint8_t *m_pImageYUV; +protected: + const unsigned int m_nFrames; + unsigned int m_nData; + uint8_t *m_pMPEG; + uint8_t *m_pImageRGB; + bool m_bUsePAL; + unsigned int m_nWidth; + unsigned int m_nHeight; + unsigned int* m_pFramesSize; + +#ifndef HAVE_FFMPEG_STATIC +private: + static void *m_hLibAvcodec; +protected: + static bool InitLibAVCodec(void); + static void CloseLibAVCodec(void); +#endif + bool m_bLoaded; +public: + cEncode(); + virtual ~cEncode(); + + /*Load Shared Library and Register Codec*/ + static bool Register(); + /*UnLoad Shared*/ + static void UnRegister(); + + bool Encode(); + inline const uint8_t *Data() const { return m_pMPEG; } + inline unsigned int Size() const { return m_nData; } + inline unsigned int GetFrames() const { return m_nFrames; } + inline unsigned int GetFrameRate() const { return m_bUsePAL?25:30; } + + inline unsigned int GetHeight() const { return m_nHeight; } + inline unsigned int GetWidth() const { return m_nWidth; } + inline unsigned int GetBorderHeight() const { return 16; } + inline unsigned int GetBorderWidth() const { return 16; } + + inline uint8_t *GetRGBMem() { return m_pImageRGB; } +#ifdef TESTCODE + bool Load(const char* szFileName); + bool Save(const char* szFileName) const; +#endif + void ClearRGBMem (); +}; + +#endif /* _ENCODE_H */ diff --git a/liboutput/stillimage-player.c b/liboutput/stillimage-player.c new file mode 100644 index 0000000..947157e --- /dev/null +++ b/liboutput/stillimage-player.c @@ -0,0 +1,61 @@ +/*************************************************************************** + * stillimage-player.c + * (C) Copyright 2004 Andreas Brachold + * Created: Thu Aug 5 2004 + * + ****************************************************************************/ + +/* + * 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 2 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "stillimage-player.h" +#include "stillimage.h" + +cStillImagePlayer::cStillImagePlayer(ePlayMode PlayMode) + : cPlayer(PlayMode) + , m_bActive(false) + , m_StillImage(this) +{ +} + +cStillImagePlayer::~cStillImagePlayer() +{ + Detach(); +} + +void cStillImagePlayer::Activate(bool On) { + if (On) { + m_bActive=m_StillImage.Init(); + } else { + if (m_bActive) { + m_StillImage.Stop(); + m_bActive=false; + } + } +} + +void cStillImagePlayer::Play(const uchar *Data, int Length) { +#if VDRVERSNUM < 10318 + PlayVideo(Data, Length); +#else + PlayPes(Data, Length,true); +#endif +} + +bool cStillImagePlayer::Wait() { + cPoller Poller; + return DevicePoll(Poller, 100); +} diff --git a/liboutput/stillimage-player.h b/liboutput/stillimage-player.h new file mode 100644 index 0000000..b3aa712 --- /dev/null +++ b/liboutput/stillimage-player.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * stillimage-player.h + * (C) Copyright 2004 Andreas Brachold + * Created: Thu Aug 5 2004 + * + ****************************************************************************/ + +/* + * 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 2 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef STILLIMAGEPLAYER_H +#define STILLIMAGEPLAYER_H + +#include +#include + +#include "stillimage.h" + +class cStillImagePlayer + : public cPlayer { + friend class cStillImage; + bool m_bActive; +protected: + cStillImage m_StillImage; +public: + cStillImagePlayer(ePlayMode PlayMode); + virtual ~cStillImagePlayer(); + + inline unsigned int UseWidth() const + { return m_StillImage.GetWidth() - (m_StillImage.GetBorderWidth()*2); } + inline unsigned int UseHeight() const + { return m_StillImage.GetHeight() - (m_StillImage.GetBorderHeight()*2); } +protected: + virtual void Activate(bool On); + void Play(const uchar *Data, int Length); + bool Wait(); + + virtual bool Worker(bool bDoIt) = 0; +}; + + +#endif diff --git a/liboutput/stillimage.c b/liboutput/stillimage.c new file mode 100644 index 0000000..9058ff0 --- /dev/null +++ b/liboutput/stillimage.c @@ -0,0 +1,210 @@ +/*************************************************************************** + * stillimage.c + * (C) Copyright 2004 + * Created: Thu Aug 5 2004 + * + * parts of the code (c) Peter Seyringer + * (c) Marcel Wiesweg + ****************************************************************************/ + +/* + * 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 2 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include +#include + +#include "stillimage.h" +#include "stillimage-player.h" + + + +cStillImage::cStillImage(cStillImagePlayer *pl) +{ + m_bThreadRun=false; + m_bEncodeRequired = false; + player=pl; +} + +cStillImage::~cStillImage() +{ + +} + +bool cStillImage::Init() +{ + return Start(); +} + +void cStillImage::Stop() +{ + if (!m_bThreadRun) + return; + m_bThreadRun=false; + + Cancel(3); +} + +void cStillImage::Action(void) +{ + m_bThreadRun=true; + bool bMPEGValid = false; + bool bQueueEmpty = false; + bool bFreeze = true; + + unsigned int nFrame = 0,nFrameOff=0; + int nMircoSekWait; + while (m_bThreadRun) { + + nMircoSekWait = 10000; + bQueueEmpty = player->Worker(false); + if(!bQueueEmpty) + { + if(!bFreeze) + { + player->DeviceFreeze(); + bFreeze = true; + } + player->Worker(true); + } + + if(m_bEncodeRequired + && bQueueEmpty) + { + //convert data to MPEG + Lock(); + bMPEGValid = Encode(); + Unlock(); + + if (!m_bThreadRun) + break; + m_bEncodeRequired = false; + nFrame = 0; + nFrameOff=0; + if(bFreeze) + { + player->DevicePlay(); + bFreeze = false; + } + + } + + + if(bMPEGValid + && bQueueEmpty) + { + /* Methode A ************************************************************/ + /*timeval timenow,diff,timestart; + gettimeofday(×tart, 0); + + for (int i=1;i<=25 && player->active;i++) { + gettimeofday(&timenow, 0); + timersub(&timenow, ×tart, &diff); + BuildPesPacket(Data(), Size(),(int)(diff.tv_sec*90000.0 + (diff.tv_usec*90000.0)/1000000.0)); + player->Wait(); + }*/ + + /* Methode B ************************************************************/ + //for (int i=1;i<= 25 && player->m_bActive;i++) { + // BuildPesPacket(Data(), Size(),i); + //} + + /* Methode C ************************************************************/ + //BuildPesPacket(Data(), Size(),1); + + /* Methode D ************************************************************/ + //player->DeviceStillPicture(Data(), Size()); + + /* Methode E ************************************************************/ + + + unsigned int nFrameSize = *(m_pFramesSize + nFrame); + if(nFrameSize) // Skip empty Frames + { + BuildPesPacket(Data() + nFrameOff, nFrameSize,1); + nFrameOff += nFrameSize; + } + if(++nFrame>=m_nFrames) + { + nFrame = 0; + nFrameOff = 0; + } + + nMircoSekWait = (1000000/(GetFrameRate()*4)); // Wait duration off 1/4 frame + + if (!m_bThreadRun) + break; + } + //Reduce CPU load!!! + usleep(max(10000,nMircoSekWait)); + } + m_bThreadRun=false; +} + + +//taken from mp1osd.c +void cStillImage::BuildPesPacket(const unsigned char *data, int len, int timestamp) { +#define PES_MAX_SIZE 2048 + int ptslen = timestamp ? 5 : 1; + static unsigned char pes_header[PES_MAX_SIZE]; + + // startcode: + pes_header[0] = pes_header[1] = 0; + pes_header[2] = 1; + pes_header[3] = 0xe0; + + while (len > 0) + { + int payload_size = len; // data + PTS + + if (6 + ptslen + payload_size > PES_MAX_SIZE) + payload_size = PES_MAX_SIZE - (6 + ptslen); + + // construct PES header: (code from ffmpeg's libav) + // packetsize: + pes_header[4] = (ptslen + payload_size) >> 8; + pes_header[5] = (ptslen + payload_size) & 255; + + if (ptslen == 5) + { + int x; + + // presentation time stamp: + x = (0x02 << 4) | (((timestamp >> 30) & 0x07) << 1) | 1; + pes_header[8] = x; + x = ((((timestamp >> 15) & 0x7fff) << 1) | 1); + pes_header[7] = x >> 8; + pes_header[8] = x & 255; + x = ((((timestamp) & 0x7fff) << 1) | 1); + pes_header[9] = x >> 8; + pes_header[10] = x & 255; + } + else + { + // stuffing and header bits: + pes_header[6] = 0x0f; + } + + memcpy (&pes_header[6 + ptslen], data, payload_size); + player->Wait(); + player->Play(pes_header, 6 + ptslen + payload_size); + + len -= payload_size; + data += payload_size; + ptslen = 1; // store PTS only once, at first packet! + + } +} diff --git a/liboutput/stillimage.h b/liboutput/stillimage.h new file mode 100644 index 0000000..453692c --- /dev/null +++ b/liboutput/stillimage.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * stillimage.h + * (C) Copyright 2004 Andreas Brachold + * Created: Thu Aug 5 2004 + * + ****************************************************************************/ + +/* + * 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 2 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef STILLIMAGE_H +#define STILLIMAGE_H + +#include +#include "encode.h" + +class cStillImagePlayer; + + +class cStillImage +: public cThread +, public cEncode { + + cStillImagePlayer *player; + volatile bool m_bThreadRun; + volatile bool m_bEncodeRequired; +protected: + virtual void Action(void); + void BuildPesPacket(const unsigned char *data, int len, int timestamp); + +public: + cStillImage(cStillImagePlayer *); + virtual ~cStillImage(); + + bool Init(); + void Stop(); + bool EncodeRequired() const {return m_bEncodeRequired;} + void EncodeRequired(bool b) {m_bEncodeRequired = b;} +}; + +#endif diff --git a/list.c b/list.c new file mode 100644 index 0000000..a96b70b --- /dev/null +++ b/list.c @@ -0,0 +1,221 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 A. Brachold + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include + +#include "list.h" +#include "data-image.h" +#include "setup-image.h" + +#include + +cActivSlideShow theSlideShow; + +cActivSlideShow::cActivSlideShow() +: m_pCurSlideShow(NULL) +, m_nCurrentImage(0) +, m_nTotalImages(0) +, m_pCurImage(NULL) +{ + +} + +void cActivSlideShow::Remove(cFileSource* src){ + + if(m_pCurSlideShow) { + cImage* p = m_pCurSlideShow->cList < cImage >::First(); + while (p) { + if(p->CompareBaseDir(src)) { + bool bRemoveCurrent = (m_pCurImage == p); + m_pCurSlideShow->cList < cImage >::Del(p); + p = m_pCurSlideShow->cList < cImage >::First(); + if(bRemoveCurrent) { + m_nCurrentImage = 1; + m_pCurImage = p; + } + } else { + p = m_pCurSlideShow->cList < cImage >::Next(p); + } + } + m_nTotalImages = m_pCurSlideShow->cList < cImage >::Count(); + } +} + +void cActivSlideShow::Assign(cSlideShow *pCurSlideShow) +{ + Shutdown(); + m_nCurrentImage = 0; + m_nTotalImages = 0; + m_pCurSlideShow = pCurSlideShow; + if(m_pCurSlideShow) { + + m_nTotalImages = m_pCurSlideShow->Count(); + // get real Index of FirstImage + unsigned int i = 0; + bool bFound = false; + if(m_pCurSlideShow->FirstImage()) { + + cImage* p = m_pCurSlideShow->cList < cImage >::First(); + + for (;p && i < m_nTotalImages; ++i) { + // find out first image + if(0 == strcmp(::basename(p->Name()),m_pCurSlideShow->FirstImage())) { + m_pCurImage = p; + bFound = true; + break; + } + p = m_pCurSlideShow->cList < cImage >::Next(p); + } + } + if(bFound) { + m_nCurrentImage = i+1; + } + else { + m_pCurImage = m_pCurSlideShow->cList < cImage >::First(); + m_nCurrentImage = 1; + } + } +} + +void cActivSlideShow::Shutdown(void) +{ + if(m_pCurSlideShow) { + delete m_pCurSlideShow; + m_pCurSlideShow = 0; + m_pCurImage = 0; + m_nCurrentImage = 0; + m_nTotalImages = 0; + } +} + +cSlideShow *cActivSlideShow::SlideShow(void) +{ + return m_pCurSlideShow; +} + + +cImage* cActivSlideShow::GetImage() +{ + return m_pCurSlideShow?m_pCurImage:NULL; +} + + +bool cActivSlideShow::NextImage(int nOffset) +{ + if(m_pCurImage && m_nCurrentImage < m_nTotalImages) { + cImage *pNewActiv; + for (int i = 0; (i < nOffset) && (m_nCurrentImage < m_nTotalImages); ++i) { + pNewActiv = m_pCurSlideShow->cList < cImage >::Next(m_pCurImage); + m_pCurImage = pNewActiv; + ++m_nCurrentImage; + } + return m_pCurImage != NULL; + } + else if(ImageSetup.AutoRepeat) { + cImage *pNewActiv; + + pNewActiv = m_pCurSlideShow->cList < cImage >::First(); + m_pCurImage = pNewActiv; + m_nCurrentImage = 1; + + return m_pCurImage != NULL; + } + return false; +} + + + +bool cActivSlideShow::PrevImage(int nOffset) +{ + if(m_pCurImage && m_nCurrentImage > 1) { + cImage *pNewActiv; + + for (int i = 0; (i < nOffset) && (m_nCurrentImage > 1); ++i) { + pNewActiv = m_pCurSlideShow->cList < cImage >::Prev(m_pCurImage); + m_pCurImage = pNewActiv; + --m_nCurrentImage; + } + return m_pCurImage != NULL; + } + else if(ImageSetup.AutoRepeat) { + cImage *pNewActiv; + + pNewActiv = m_pCurSlideShow->cList < cImage >::Last(); + m_pCurImage = pNewActiv; + m_nCurrentImage = m_nTotalImages; + return m_pCurImage != NULL; + } + return false; +} + + +bool cActivSlideShow::GotoImage(unsigned int nNewPictureIndex) +{ + if(m_pCurImage && nNewPictureIndex > 0 && nNewPictureIndex < m_nTotalImages) { + cImage *pNewActiv; + pNewActiv = m_pCurSlideShow->cList < cImage >::First(); + m_pCurImage = pNewActiv; + m_nCurrentImage = 0; + for (unsigned int i = 0; i < nNewPictureIndex; ++i) { + pNewActiv = m_pCurSlideShow->cList < cImage >::Next(m_pCurImage); + m_pCurImage = pNewActiv; + ++m_nCurrentImage; + } + return m_pCurImage != NULL; + } + return false; +} + +int cActivSlideShow::GetJumpNames(int nOffset,cImage* pImage[],const unsigned int nMAX_BILDER) +{ + unsigned int i; + cImage *pNewActiv; + int nBilder = 0; + + if(m_pCurImage && m_nCurrentImage > 0) { + + int nJumpFirst = m_nCurrentImage + nOffset; + + if(nJumpFirst > ((int)(m_nTotalImages - nMAX_BILDER + 1))) + nJumpFirst = ((int)(m_nTotalImages - nMAX_BILDER + 1)); + if(nJumpFirst < 1) + nJumpFirst = 1; + + m_nCurrentImage = nJumpFirst; + if(m_nCurrentImage > m_nTotalImages) + m_nCurrentImage = m_nTotalImages; + if(m_nCurrentImage < 1) + m_nCurrentImage = 1; + + pNewActiv = m_pCurSlideShow->cList < cImage >::First(); + for (int n = 1; n < nJumpFirst && pNewActiv; ++n) + pNewActiv = m_pCurSlideShow->cList < cImage >::Next(pNewActiv); + for (i = 0; (i < nMAX_BILDER) && ((nJumpFirst + i) <= m_nTotalImages) && pNewActiv; ++i,++nBilder) { + + pImage[i] = pNewActiv; + dsyslog("imageplugin: File%d: %s", i, pNewActiv->Name()); + if((nJumpFirst + i) == m_nCurrentImage) + m_pCurImage = pNewActiv; + pNewActiv = m_pCurSlideShow->cList < cImage >::Next(pNewActiv); + } + } + return nBilder; +} diff --git a/list.h b/list.h new file mode 100644 index 0000000..97824ca --- /dev/null +++ b/list.h @@ -0,0 +1,60 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef ___IMAGE_LIST_H +#define ___IMAGE_LIST_H + +class cSlideShow; +class cImage; +class cFileSource; + +class cActivSlideShow +{ + /** The current active filelist with the slide*/ + cSlideShow *m_pCurSlideShow; + /** Current number of Image in slide */ + unsigned int m_nCurrentImage; + /** Total number of Images in slide */ + unsigned int m_nTotalImages; + /** Reference to current Image of this player */ + cImage *m_pCurImage; + +public: + cActivSlideShow(); + void Assign(cSlideShow *pCurSlideShow); + void Shutdown(void); + cSlideShow *SlideShow(void); + cImage* GetImage(); + bool PrevImage(int nOffset); + bool GotoImage(unsigned int nNewPictureIndex); + bool NextImage(int nOffset); + int GetJumpNames(int nOffset,cImage* pImage[],const unsigned int nMAX_BILDER); + + void Remove(cFileSource* src); + + /** Deliver the current number of viewed Image */ + int ImageCurrent(void) const { return m_nCurrentImage; } + /** Deliver the total number of viewed Image */ + int ImageTotal(void) const { return m_nTotalImages; } + +} extern theSlideShow; + +#endif //___IMAGE_LIST_H diff --git a/menu-commands.c b/menu-commands.c new file mode 100644 index 0000000..8aaaaec --- /dev/null +++ b/menu-commands.c @@ -0,0 +1,163 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include "menu-commands.h" +#include "i18n.h" + +cImageMenuCommands::cImageMenuCommands(const char *szTitle, cImageCommands *pCmds, const char *szFileName /*= NULL*/) +: cOsdMenu(m_szTitle = strdup(szTitle)) +, m_pCmds( pCmds ) +, m_szFileName(NULL) +, m_bImageChanged(false) +{ + SetHasHotkeys(); + if(szFileName) + m_szFileName = strdup(szFileName); + + int i = 0; + cImageCommand *p = NULL; + for(;NULL != (p = m_pCmds->Get(i));++i) + { + Add(new cOsdItem(hk(p->Title()))); + } + SetHelp(tr("Execute"), NULL, NULL, tr("Back")); + Display(); +} + +cImageMenuCommands::~cImageMenuCommands() +{ + if(m_szTitle) + free(m_szTitle); + if(m_szFileName) + free(m_szFileName); + + if(m_pCmds) + delete m_pCmds; +} + +eOSState cImageMenuCommands::Execute(void) +{ + errno = 0; // unset error + char *sz = NULL; + cImageCommand *p; + struct stat stFile,stChanged; + if(0 == stat(m_szFileName, &stFile) + && NULL != (p = m_pCmds->Get(Current()))) + { + bool bConfirmed = true; + if (p->Confirm()) { + asprintf(&sz, "%s?", p->Title()); + bConfirmed = Interface->Confirm(sz); + free(sz); + } + if (bConfirmed) { + asprintf(&sz, "%s...", p->Title()); + OSD_InfoMsg(sz); + free(sz); + const char *szResult = p->Execute(m_szFileName); + if(0 == stat(m_szFileName, &stChanged)) + { + m_bImageChanged = stFile.st_mtime != stChanged.st_mtime; + } + if (szResult) + return AddSubMenu(new cImageMenuResult(p->Title(), szResult, fontFix)); + return osEnd; + } + return osContinue; + } + const char* szErr = errno?strerror(errno):tr("Operation failed"); + asprintf(&sz, "%s (%s)", szErr,m_szFileName); + OSD_ErrorMsg(sz); + free(sz); + return osContinue; +} + +eOSState cImageMenuCommands::ProcessKey(eKeys nKey) +{ + eOSState state = cOsdMenu::ProcessKey(nKey); + + if (state == osUnknown) { + switch (nKey) { + case kRed: + case kOk: return Execute(); + case kMenu: + case kBack: + case kBlue: + return osBack; + default: break; + } + } + return state; +} + + + +cImageMenuResult::cImageMenuResult(const char *szTitle, const char *szText, eDvbFont Font) +: cOsdMenu(szTitle) +, m_szText(szText) +{ +#if VDRVERSNUM < 10307 + Add(new cMenuTextItem(szText, 1, 2, Setup.OSDwidth - 2, MAXOSDITEMS, clrWhite, clrBackground, Font)); +#endif + SetHelp(NULL, NULL, NULL, tr("Back")); +} + + +eOSState cImageMenuResult::ProcessKey(eKeys nKey) +{ +#if VDRVERSNUM >= 10307 + switch (nKey) { + case kUp|k_Repeat: + case kUp: + case kDown|k_Repeat: + case kDown: + case kLeft|k_Repeat: + case kLeft: + case kRight|k_Repeat: + case kRight: + DisplayMenu()->Scroll(NORMALKEY(nKey) == kUp || NORMALKEY(nKey) == kLeft, NORMALKEY(nKey) == kLeft || NORMALKEY(nKey) == kRight); + return osContinue; + default: break; + } +#endif + + eOSState nState = cOsdMenu::ProcessKey(nKey); + if (nState == osUnknown) { + switch (nKey) { + case kOk: + case kMenu: + case kBack: + case kBlue: + return osBack; + default: nState = osContinue; + } + } + return nState; +} + +#if VDRVERSNUM >= 10307 +void cImageMenuResult::Display(void) +{ + cOsdMenu::Display(); + DisplayMenu()->SetText(m_szText, true); +} +#endif diff --git a/menu-commands.h b/menu-commands.h new file mode 100644 index 0000000..07b7c33 --- /dev/null +++ b/menu-commands.h @@ -0,0 +1,60 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef __MENU_COMMANDS_H +#define __MENU_COMMANDS_H + +#include "image.h" +#include "commands.h" +#include +#include + +class cImageMenuCommands +: public cOsdMenu +{ + cImageCommands *m_pCmds; + char *m_szFileName; + bool m_bImageChanged; + char *m_szTitle; +protected: + eOSState Execute(void); +public: + cImageMenuCommands(const char *szTitle, cImageCommands *Cmds, const char *szFileName = NULL); + virtual ~cImageMenuCommands(); + virtual eOSState ProcessKey(eKeys Key); + bool HasImageChanged() const { return m_bImageChanged; } +}; + + +class cImageMenuResult : public cOsdMenu +{ + const char *m_szText; +protected: +#if VDRVERSNUM >= 10307 + virtual void Display(void); +#endif +public: + cImageMenuResult(const char *Title, const char *Text, eDvbFont Font = fontOsd); + virtual eOSState ProcessKey(eKeys Key); +}; + + +#endif //__MENU_COMMANDS_H diff --git a/menu-image.c b/menu-image.c new file mode 100644 index 0000000..52e2e83 --- /dev/null +++ b/menu-image.c @@ -0,0 +1,115 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2003 Kai Tobias Burwieck + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "image.h" +#include "menu.h" +#include "data-image.h" +#include "menu-image.h" +#include "control-image.h" +#include "i18n.h" + +#include + + +// --- cMenuImageBrowse --------------------------------------------------------- + +cMenuImageBrowse::cMenuImageBrowse(void) +: cMenuBrowse(ImageSources.GetSource(), true,tr("Image browser")) +{ + sourcing = false; + SetButtons(); +} + +void cMenuImageBrowse::SetButtons(void) +{ + SetHelp(tr("Play"), 0, tr("Source"), currentdir ? tr("Parent") : 0); + Display(); +} + +eOSState cMenuImageBrowse::Source(bool second) +{ + if(HasSubMenu()) + return osContinue; + + if(!second) { + sourcing = true; + return AddSubMenu(new + cMenuSource(&ImageSources, tr("Image source"))); + } + sourcing = false; + cFileSource *src = cMenuSource::GetSelected(); + if(src) { + ImageSources.SetSource(src); + SetSource(src); + NewDir(0); + } + return osContinue; +} + +eOSState cMenuImageBrowse::ProcessKey(eKeys Key) +{ + eOSState state = cMenuBrowse::ProcessKey(Key); + + if(!HasSubMenu() && state == osContinue) { + // eval the return value from submenus + if(sourcing) + return Source(true); + } + + if(state == osBack && lastselect) { + char *name = lastselect->Path(); + char *full = source->BuildName(name); + cDirItem *item = cMenuBrowse::GetSelected(); + if(item) { + + //FIXME use a nonblocking way + //OSD_InfoMsg(tr("Building SlideShow...")); + + cSlideShow *newss = new cSlideShow(item); + if(newss->Load() && newss->Count()) { + + cImageControl::SetSlideShow(newss); + state = osEnd; + } + else { + OSD_ErrorMsg(tr("No Files!")); + delete newss; + state = osContinue; + } + lastselect = NULL; + } + free(full); + free(name); + } + if(state == osUnknown && Key == kYellow) + return Source(false); + return state; +} diff --git a/menu-image.h b/menu-image.h new file mode 100644 index 0000000..86442d7 --- /dev/null +++ b/menu-image.h @@ -0,0 +1,41 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2003 Kai Tobias Burwieck + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef ___MENU_IMAGE_H +#define ___MENU_IMAGE_H + +#include +#include + +#include "menu.h" + +// ---------------------------------------------------------------- +class cMenuImageBrowse:public cMenuBrowse { + private: + bool sourcing; + void SetButtons(void); + eOSState Source(bool second); + public: + cMenuImageBrowse(void); + virtual eOSState ProcessKey(eKeys Key); +}; + +#endif //___MENU_IMAGE_H diff --git a/menu.c b/menu.c new file mode 100644 index 0000000..96c1a12 --- /dev/null +++ b/menu.c @@ -0,0 +1,402 @@ +/* + * image plugin to VDR (C++) + * + * (C) 2004 "Interpohl" + * (C) 2004 A.Brachold + * (C) 2004 O.Kreuzinger + * (C) 2003 Kai Tobias Burwieck + * (C) 2001,2002 Stefan Huelswitt + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "menu.h" +#include "i18n.h" + +// --- cMenuBrowseItem ---------------------------------------------------------- + +class cMenuBrowseItem:public cOsdItem { +private: + cDirItem * item; + virtual void Set(void); +public: + cMenuBrowseItem(cDirItem * Item); + cDirItem *Item(void) { return item; } +}; + +cMenuBrowseItem::cMenuBrowseItem(cDirItem * Item) +{ + item = Item; + Set(); +} + +void cMenuBrowseItem::Set(void) +{ + char *buffer = 0; + asprintf(&buffer, item->Type == itFile ? "%s" : "[%s]", item->Name); + SetText(buffer, false); +} + +// --- cMenuBrowse ------------------------------------------------------ + +cDirItem *cMenuBrowse::lastselect = 0; + +cMenuBrowse::cMenuBrowse(cFileSource * Source, bool Dirselect,const char *title) +: cOsdMenu(title) +{ + currentdir = parent = 0; + delete lastselect; + lastselect = 0; + list = new cDirList; + + dirselectable = Dirselect; + + SetSource(Source); + LoadDir(currentdir); + SetButtons(); +} + +cMenuBrowse::~cMenuBrowse() +{ + free(parent); + free(currentdir); + delete list; +} + +cDirItem *cMenuBrowse::CurrentItem(void) +{ + cMenuBrowseItem *item = (cMenuBrowseItem *) Get(Current()); + return item ? item->Item() : 0; +} + +void cMenuBrowse::SetButtons(void) +{ + SetHelp(tr("Select"), 0, 0, currentdir ? tr("Parent") : 0); + Display(); +} + +void cMenuBrowse::SetSource(cFileSource * Source) +{ + source = Source; + free(currentdir); + currentdir = 0; + free(parent); + parent = 0; + source->GetRemember(currentdir, parent); +} + +bool cMenuBrowse::LoadDir(const char *dir) +{ + Clear(); + + //FIXME use a nonblocking way + //OSD_InfoMsg(tr("Scanning directory...")); + + if(!list->Load(source, dir)) + return false; + + cDirItem *item = list->First(); + while(item) + { + Add(new cMenuBrowseItem(item), + (parent && !strcmp(item->Name, parent))); + item = list->Next(item); + } + return true; +} + +bool cMenuBrowse::NewDir(const char *dir) +{ + if(LoadDir(dir)) + { + char *ncur = dir ? strdup(dir) : 0; // don't free currentdir first, as (new)dir may + free(currentdir); + currentdir = ncur; // be a reference to somewhere inside currentdir as well + + cDirItem *item = CurrentItem(); + source->SetRemember(currentdir, item ? item->Name : 0); + + SetButtons(); + return true; + } + OSD_ErrorMsg(tr("Error scanning directory!")); + return false; +} + +eOSState cMenuBrowse::Parent(void) +{ + eOSState res = osContinue; + + if(currentdir) + { + free(parent); + char *ss = strrchr(currentdir, '/'); + if(ss) + { + *ss++ = 0; + parent = strdup(ss); + ss = currentdir; + } + else + parent = strdup(currentdir); + + if(!NewDir(ss)) + res = osEnd; + } + return res; +} + +eOSState cMenuBrowse::Select(bool isred) +{ + eOSState res = osContinue; + cDirItem *item; + + if((item = CurrentItem())) + { + switch (item->Type) + { + case itParent: + if(!isred || !dirselectable) + res = Parent(); + break; + case itDir: + if(!isred || !dirselectable) + { + char *d = item->Path(); + if(!NewDir(d)) + res = osEnd; + free(d); + break; + } + // fall through to itFile + case itFile: + lastselect = + new cDirItem(source, currentdir, item->Name, + item->Type); + res = osBack; + break; + default: + break; + } + } + return res; +} + +eOSState cMenuBrowse::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + + if(state == osUnknown) + { + switch (Key) + { + case kOk: + state = Select(false); + break; + case kRed: + state = Select(true); + break; + case kBlue: + state = Parent(); + break; + case kMenu: + state = osEnd; + break; + default: + break; + } + } + if(state == osEnd || state == osBack) + { + cDirItem *item = CurrentItem(); + if(item) + source->SetRemember(currentdir, item->Name); + } + return state; +} + +// --- cMenuSourceItem ---------------------------------------------------------- + +class cMenuSourceItem:public cOsdItem { + private: + cFileSource * source; + virtual void Set(void); + public: + cMenuSourceItem(cFileSource * Source); + cFileSource *Source(void) { return source; } +}; + +cMenuSourceItem::cMenuSourceItem(cFileSource * Source) +{ + source = Source; + Set(); +} + +void cMenuSourceItem::Set(void) +{ + char *buffer = 0; + asprintf(&buffer, "%s\t%s\t%s", + source->NeedsMount()? (source->Status()? "*" : ">") : "", + source->Description(), source->BaseDir()); + SetText(buffer, false); +} + +// --- cMenuSource -------------------------------------------------- + +cFileSource *cMenuSource::selected = 0; + +cMenuSource::cMenuSource(cFileSources * Sources, + const char *title):cOsdMenu(title, 2, 20) +{ + selected = 0; + current = Sources->GetSource(); + cFileSource *source = Sources->First(); + while(source) { + cOsdMenu::Add(new cMenuSourceItem(source), source == current); + source = Sources->Next(source); + } + + SetHelp(tr("Select"), tr("Mount"), tr("Unmount"), tr("Eject")); + Display(); +} + +bool cMenuSource::DoMount(cFileSource * src) +{ + bool res = src->Mount(); + RefreshCurrent(); + DisplayCurrent(true); + return res; +} + +bool cMenuSource::CheckMount(void) +{ + cFileSource *src = selected ? selected : current; + if(src->NeedsMount() && !src->Status()) { + OSD_ErrorMsg(tr("Selected source is not mounted!")); + return false; + } + return true; +} + +eOSState cMenuSource::Select(void) +{ + if(HasSubMenu() || Count() == 0) + return osContinue; + + cFileSource *src = ((cMenuSourceItem *) Get(Current()))->Source(); + if(src->NeedsMount() && !src->Status()) + { + if(!DoMount(src)) + OSD_ErrorMsg(tr("Mount failed!")); + } + if(!src->NeedsMount() || src->Status()) + { + selected = src; + return osBack; + } + return osContinue; +} + +eOSState cMenuSource::Mount(void) +{ + if(HasSubMenu() || Count() == 0) + return osContinue; + + cFileSource *src = ((cMenuSourceItem *) Get(Current()))->Source(); + if(src->NeedsMount() && !src->Status()) + { + if(DoMount(src)) + OSD_InfoMsg(tr("Mount succeeded")); + else + OSD_ErrorMsg(tr("Mount failed!")); + } + return osContinue; +} + +eOSState cMenuSource::Unmount(void) +{ + if(HasSubMenu() || Count() == 0) + return osContinue; + + cFileSource *src = ((cMenuSourceItem *) Get(Current()))->Source(); + if(src->NeedsMount() && src->Status()) + { + bool res = src->Unmount(); + RefreshCurrent(); + DisplayCurrent(true); + if(res) + OSD_InfoMsg(tr("Unmount succeeded")); + else + OSD_ErrorMsg(tr("Unmount failed!")); + } + return osContinue; +} + +eOSState cMenuSource::Eject(void) +{ + if(HasSubMenu() || Count() == 0) + return osContinue; + + cFileSource *src = ((cMenuSourceItem *) Get(Current()))->Source(); + if(src->NeedsMount()) + { + bool res = src->Eject(); + RefreshCurrent(); + DisplayCurrent(true); + if(!res) + OSD_ErrorMsg(tr("Eject failed!")); + } + return osContinue; +} + +eOSState cMenuSource::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + + if(state == osBack && !CheckMount()) + state = osContinue; + if(state == osUnknown) + { + switch (Key) + { + case kOk: + case kRed: + return Select(); + case kGreen: + return Mount(); + case kYellow: + return Unmount(); + case kBlue: + return Eject(); + case kMenu: + CheckMount(); + return osEnd; + default: + break; + } + } + return state; +} diff --git a/menu.h b/menu.h new file mode 100644 index 0000000..263d879 --- /dev/null +++ b/menu.h @@ -0,0 +1,75 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2003 Kai Tobias Burwieck + * + * based on MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef ___MENU_H +#define ___MENU_H + +#include "image.h" +#include "data.h" +// ---------------------------------------------------------------- + +class cMenuBrowse:public cOsdMenu { + private: + eOSState Select(bool isred); + eOSState Parent(void); + bool LoadDir(const char *dir); + protected: + static cDirItem *lastselect; + // + cDirList *list; + cFileSource *source; + bool dirselectable; + char *currentdir, *parent; +// + bool NewDir(const char *dir); + void SetSource(cFileSource * Source); + cDirItem *CurrentItem(void); + virtual void SetButtons(void); + public: + cMenuBrowse(cFileSource * Source, bool Dirselect, const char *title); + ~cMenuBrowse(); + virtual eOSState ProcessKey(eKeys Key); + static cDirItem *GetSelected(void) {return lastselect;} +}; + +// ---------------------------------------------------------------- + +class cMenuSource:public cOsdMenu { + private: + static cFileSource *selected; + cFileSource *current; + // + eOSState Mount(void); + eOSState Unmount(void); + eOSState Eject(void); + eOSState Select(void); + bool DoMount(cFileSource * src); + bool CheckMount(void); + public: + cMenuSource(cFileSources * Sources, const char *title); + virtual eOSState ProcessKey(eKeys Key); + static cFileSource *GetSelected(void) { return selected;} +}; + +#endif //___MENU_H diff --git a/player-image.c b/player-image.c new file mode 100644 index 0000000..f8818f8 --- /dev/null +++ b/player-image.c @@ -0,0 +1,478 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold + * 2003 Kai Tobias Burwieck + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "player-image.h" +#include "control-image.h" +#include "setup-image.h" +#include "data-image.h" +#include "image.h" +#include "list.h" +#include "i18n.h" + +#include "libimage/pnm.h" +#include "libimage/xpm.h" + + +const char *g_szConvertScript = "imageplugin.sh"; + + +//----------cImagePlayer------------- + + + + +cImagePlayer::cImagePlayer(cSlideShow *pCurSlideShow) +: cStillImagePlayer( +#if VDRVERSNUM >= 10308 + (ImageSetup.m_bLiveAudio != 0)?pmVideoOnly: +#endif + pmAudioVideo + ) +, m_bConvertRunning(false) +, m_szError(NULL) +{ + theSlideShow.Assign(pCurSlideShow); +} + +bool cImagePlayer::GetIndex(int &nCurrent, int &nTotal, bool /*bSnapToIFrame*/) +{ + // Notify other status-plugins with one picture as one second + nCurrent = theSlideShow.ImageCurrent(); + // Notify other status-plugins count of picture as totalcount of seconds + nTotal = theSlideShow.ImageTotal(); + + return true; +} + +cImagePlayer::~cImagePlayer() +{ + // Remove Slideshow + theSlideShow.Shutdown(); +} + + +void cImagePlayer::Activate(bool bOn) +{ + if(bOn) { + if(theSlideShow.GetImage()) + { + cStillImagePlayer::Activate(bOn); + Convert(""); + } + } + else + cStillImagePlayer::Activate(false); +} + + +bool cImagePlayer::NextImage(int nOffset) +{ + return theSlideShow.NextImage(nOffset); +} + + + +bool cImagePlayer::PrevImage(int nOffset) +{ + return theSlideShow.PrevImage(nOffset); +} + + +bool cImagePlayer::GotoImage(unsigned int nNewPictureIndex) +{ + return theSlideShow.GotoImage(nNewPictureIndex); +} + + +bool cImagePlayer::Convert(const char *szChange) +{ + cImage* pImage = theSlideShow.GetImage(); + if(pImage) + { + cShellWrapper* pCmd = new cShellWrapper; + pCmd->bClearBackground = true; + pCmd->nOffLeft = m_StillImage.GetBorderWidth(); + pCmd->nOffTop = m_StillImage.GetBorderHeight(); + pCmd->nWidth = UseWidth(); + pCmd->nHeight = UseHeight(); + + // Build image_convert.sh "source.jpg" "/tmp/image/dest.pnm" 720 576 0 0 0 0 original + pCmd->szPNM = strdup(pImage->NamePNM()); + + asprintf(&pCmd->szCmd, "%s \"%s\" \"%s\" %d %d %d %d %d %s", + g_szConvertScript, + pImage->Name(), + pCmd->szPNM, + pCmd->nWidth, + pCmd->nHeight, + 0, + 0, + 0, + szChange ? szChange : ""); + + Exec(pCmd); + return true; + } + return false; +} + + + +bool cImagePlayer::ConvertJump(int nOffset) +{ + register unsigned int w,h; + const unsigned int MAX_BILDER = 9; + cImage* pImage[MAX_BILDER]; + for (w = 0; w < MAX_BILDER; ++w) + pImage[w] = NULL; + int nBilder = theSlideShow.GetJumpNames(nOffset,pImage,MAX_BILDER); + if(nBilder > 0 + && pImage[0]) { + + unsigned int nMatrix = (nBilder < 5) ? 2 : 3; + + for (h = 0; h < nMatrix; ++h) + for (w = 0; w < nMatrix && pImage[(h*nMatrix)+w]; ++w) + { + cShellWrapper* pCmd = new cShellWrapper; + + pCmd->bClearBackground = (w == 0 && h == 0); + pCmd->nWidth = UseWidth(); + pCmd->nHeight = UseHeight(); + pCmd->nWidth /= nMatrix; + pCmd->nHeight /= nMatrix; + pCmd->nOffLeft = (pCmd->nWidth * w) + m_StillImage.GetBorderWidth(); + pCmd->nOffTop = (pCmd->nHeight * h) + m_StillImage.GetBorderHeight(); + + // Build image_convert.sh "source.jpg" "/tmp/image/source.jpg-i.pnm" 256 192 0 0 0 0 original + pCmd->szPNM = strdup(pImage[(h*nMatrix)+w]->NameIndex()); + + + + asprintf(&pCmd->szCmd, "%s \"%s\" \"%s\" %d %d %d %d %d %s", + g_szConvertScript, + pImage[(h*nMatrix)+w]->Name(), + pCmd->szPNM, + pCmd->nWidth, + pCmd->nHeight, + 0, + 0, + 0, + /*szChange ? szChange : */""); + pCmd->szNumber = '0'+((h*nMatrix)+w)+1; + + Exec(pCmd); + } + return true; + } + return false; +} + + +bool cImagePlayer::ConvertZoom(const char *szChange, int nZoomFaktor, + int nLeftPos, int nTopPos) +{ + cImage* pImage = theSlideShow.GetImage(); + if(pImage) + { + cShellWrapper* pCmd = new cShellWrapper; + pCmd->bClearBackground = true; + pCmd->nOffLeft = m_StillImage.GetBorderWidth() + (nLeftPos>0?0:(nLeftPos*-1)); + pCmd->nOffTop = m_StillImage.GetBorderHeight() + (nTopPos>0?0:(nTopPos*-1)); + pCmd->nWidth = UseWidth(); + pCmd->nHeight = UseHeight(); + + // Build image_convert.sh "source.jpg" "/tmp/image/dest.pnm" 720 576 0 0 0 0 original + pCmd->szPNM = strdup(pImage->NameZoom()); + + asprintf(&pCmd->szCmd, "%s \"%s\" \"%s\" %d %d %d %d %d %s", + g_szConvertScript, + pImage->Name(), + pCmd->szPNM, + pCmd->nWidth, + pCmd->nHeight, + nZoomFaktor, + nLeftPos>0?nLeftPos:0, + nTopPos>0?nTopPos:0, + szChange ? szChange : ""); + + Exec(pCmd); + return true; + } + return false; +} + + +void cImagePlayer::LoadImage(cShellWrapper* pShell) +{ + cPNM pnmImage; + bool bSuccess = false; + register unsigned int nHeight = m_StillImage.GetHeight(); + register unsigned int nWidth = m_StillImage.GetWidth(); + register unsigned int nOffLeft = 0; + register unsigned int nOffTop = 0; + errno = 0; + if(!pShell || pShell->bClearBackground) + m_StillImage.ClearRGBMem(); + + if(pShell && pShell->szPNM) + { + nHeight = std::min(nHeight,pShell->nHeight); + nWidth = std::min(nWidth,pShell->nWidth); + nOffLeft = pShell->nOffLeft; + nOffTop = pShell->nOffTop; + + FILE *f=fopen(pShell->szPNM, "r"); + if(f) + { + xel* pRow = NULL; + register unsigned int w; + register unsigned int h; + + if(pnmImage.readHeader(f)) + { + register unsigned int nColorDepth = pnmImage.GetColorDepth(); + + if(pnmImage.GetWidth() < nWidth) + nOffLeft += (nWidth - pnmImage.GetWidth()) / 2; + + if(pnmImage.GetHeight() < nHeight) + nOffTop += (nHeight - pnmImage.GetHeight()) / 2; + + + for(h = 0; + h < pnmImage.GetHeight() + && h < nHeight + && h+nOffTop < m_StillImage.GetHeight(); + ++h) + { + if(!pnmImage.allocrow(&pRow) + ||!pnmImage.readrow(f, pRow) ) + break; + + xel* pP = pRow; + for(w = 0;w < pnmImage.GetWidth() + && w < nWidth + && w+nOffLeft < m_StillImage.GetWidth(); + ++w,++pP) + { + uint8_t* pImageRGB = m_StillImage.GetRGBMem() + + ((((h+nOffTop)*m_StillImage.GetWidth())+w+nOffLeft)*3); + + if(nColorDepth == 0xFF) // normal 8-bit at any canal (24 Bit) colordepth + { + *(pImageRGB + 0) = (uint8_t) PPM_GETR(*pP); + *(pImageRGB + 1) = (uint8_t) PPM_GETG(*pP); + *(pImageRGB + 2) = (uint8_t) PPM_GETB(*pP); + } + else if(nColorDepth == 1) // black/white image + { + *(pImageRGB + 0) = (uint8_t) PPM_GETR(*pP)==0?0x00:0xFF; + *(pImageRGB + 1) = (uint8_t) PPM_GETG(*pP)==0?0x00:0xFF; + *(pImageRGB + 2) = (uint8_t) PPM_GETB(*pP)==0?0x00:0xFF; + } + else // Adjust other colordepth to 8bit + { + *(pImageRGB + 0) = (uint8_t) (PPM_GETR(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF; + *(pImageRGB + 1) = (uint8_t) (PPM_GETG(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF; + *(pImageRGB + 2) = (uint8_t) (PPM_GETB(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF; + } + } + pnmImage.freerow(pRow); + pRow = NULL; + } + if(pRow) + pnmImage.freerow((char*)pRow); + else + { + if(pShell->szNumber && ImageSetup.ShowNumbers) + cXPM::Overlay(pShell->szNumber,m_StillImage.GetRGBMem(), + m_StillImage.GetWidth(),m_StillImage.GetHeight(), + cXPM::TopRight,nOffLeft,nOffTop,pnmImage.GetWidth(),pnmImage.GetHeight()); + + bSuccess = true; + } + } + fclose(f); + } + } + if(!bSuccess) { + + // Merge Errorimage with Encoder-Memory + if(pShell && pShell->szNumber) + cXPM::Overlay('s',m_StillImage.GetRGBMem(), + m_StillImage.GetWidth(),m_StillImage.GetHeight(), + cXPM::Center,pShell->nOffLeft,pShell->nOffTop,pShell->nWidth,pShell->nHeight); + else + cXPM::Error(m_StillImage.GetRGBMem(), + m_StillImage.GetWidth(),m_StillImage.GetHeight()); + + // Build Error, depends PNM Errormsg or system messages + char szErr[128]; + + if(pnmImage.GetError()) + strncpy(szErr,pnmImage.GetError(),sizeof(szErr)); + else if(errno) { + int nErr = errno; + szErr[sizeof(szErr)-1] = '\0'; + if(0 != strerror_r(nErr,szErr,sizeof(szErr)-1)) { + szErr[0] = '\0'; + } + } + + // Make Syslog entry + if(pShell && pShell->szPNM && szErr) + esyslog("imageplugin: Error until read %s : '%s'", pShell->szPNM, szErr); + else if(pShell && pShell->szPNM) + esyslog("imageplugin: Error until read %s", pShell->szPNM); + else + esyslog("imageplugin: Error until read image %s",szErr?szErr:""); + + { // Copy Errormessage forward to OSD Thread + cMutexLock lock(&m_MutexErr); + if(m_szError) + free(m_szError); + if(szErr) + asprintf(&m_szError, "%s : %s", tr("Image couldn't load"),szErr); + else + m_szError = strdup(tr("Image couldn't load")); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/** void cImagePlayer::ExecFailed() +Show a precompiled Image if Exec can't find a converted Image +@param - char* szErr +@return - nothing */ +void cImagePlayer::ExecFailed(cShellWrapper* pShell,const char* szErr) +{ + if(!pShell || pShell->bClearBackground) + m_StillImage.ClearRGBMem(); + + // Merge Errorimage with Encoder-Memory + if(pShell && pShell->szNumber) + cXPM::Overlay('s',m_StillImage.GetRGBMem(), + m_StillImage.GetWidth(),m_StillImage.GetHeight(), + cXPM::Center,pShell->nOffLeft,pShell->nOffTop,pShell->nWidth,pShell->nHeight); + else + cXPM::Error(m_StillImage.GetRGBMem(), + m_StillImage.GetWidth(),m_StillImage.GetHeight()); + + + // Store Message for OSD-Thread + { + cMutexLock lock(&m_MutexErr); + if(m_szError) + free(m_szError); + m_szError = strdup(szErr); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/** void cImagePlayer::ErrorMsg() +ThreadSafe Method to show messages from Worker thread, +this functions is called only from cImageControl::ProcessKey(kNone) +@return - nothing */ +void cImagePlayer::ErrorMsg() +{ + char* szErr = NULL; + { + cMutexLock lock(&m_MutexErr); + szErr = m_szError; + m_szError = NULL; + } + if(szErr) + { + OSD_ErrorMsg(szErr); + free(szErr); + } +} + +const char* cImagePlayer::FileName(void) const +{ + cImage* pImage = theSlideShow.GetImage(); + return pImage?pImage->Name():NULL; +} + +void cImagePlayer::Exec(cShellWrapper* pCmd) +{ + if(pCmd->szPNM) + m_bConvertRunning = true; + if(pCmd) { + cMutexLock lock(&m_Mutex); + if(!m_queue.add(pCmd)) + delete pCmd; //Queue full or Thread is dead + } +} + +bool cImagePlayer::Worker(bool bDoIt) +{ + bool bQueueEmpty; + cShellWrapper *pShell = NULL; + + { // Protect the queue ++ + cMutexLock lock(&m_Mutex); + if(bDoIt && !m_queue.empty()) { + pShell = *m_queue.begin(); + m_queue.erase(m_queue.begin()); + } + bQueueEmpty = m_queue.empty(); + } // ++ + + if(!pShell) + { + m_bConvertRunning = m_StillImage.EncodeRequired(); + return bQueueEmpty; + } + + if(pShell->szCmd) { + //dsyslog("imageplugin: executing script '%s'", pShell->szCmd); + + ImageSetup.SetEnv(); + if(0 == SystemExec(pShell->szCmd)) + { + if(pShell->szPNM) { + LoadImage(pShell); + m_StillImage.EncodeRequired(true); + } + } + else + { + esyslog("imageplugin: script execution failed '%s'", pShell->szCmd); + if(pShell->szPNM) { + ExecFailed(pShell,tr("Script execution failed")); + m_StillImage.EncodeRequired(true); + } + } + } + delete pShell; + return bQueueEmpty; +} diff --git a/player-image.h b/player-image.h new file mode 100644 index 0000000..6a784de --- /dev/null +++ b/player-image.h @@ -0,0 +1,148 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold + * 2003 Kai Tobias Burwieck + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef ___DVB_IMAGE_H +#define ___DVB_IMAGE_H + +#include +#define __STL_CONFIG_H + +#include +#include +#include + +class cImage; +class cSlideShow; + +struct cShellWrapper { + + char* szCmd; + char* szPNM; + char szNumber; + bool bClearBackground; + unsigned int nOffLeft; + unsigned int nOffTop; + unsigned int nWidth; + unsigned int nHeight; + + cShellWrapper() + : szCmd(NULL) + , szPNM(NULL) + , szNumber('\0') + { + } + + virtual ~cShellWrapper() { + if(szPNM) + free(szPNM); + if(szCmd) + free(szCmd); + } +}; + +struct cShellWrapperQueue + : public std::vector +{ + virtual ~cShellWrapperQueue() + { + iterator i = begin(); + const_iterator e = end(); + for(;e!=i;++i) + delete(*i); + clear(); + } + + inline size_t max_size() const + { + return 64; + } + + inline bool add(cShellWrapper* pCmd) { + if(size()szPNM || !pCmd->bClearBackground) //Pregeneration or Index + push_back(pCmd); + else { + // Remove all other viewed images from queue + iterator i = begin(); + while(end()!=i) { + if((*i)->szPNM) { + delete(*i); + erase(i); + } + else + ++i; + } + //Place next viewed image at front of the queue + insert(begin(),pCmd); + } + return true; + } + return false; + } + +}; + +class cImagePlayer +: public cStillImagePlayer +{ + + volatile bool m_bConvertRunning; + + cMutex m_Mutex; + cShellWrapperQueue m_queue; + cMutex m_MutexErr; + char* m_szError; +protected: + void Exec(cShellWrapper* pCmd); + + void LoadImage(cShellWrapper* pShell); + /** Show Errorimage if operation failed*/ + void ExecFailed(cShellWrapper* pShell,const char* szErr); + + virtual void Activate(bool On); + virtual bool Worker(bool bDoIt); +public: + + cImagePlayer(cSlideShow *pCurSlideShow); + virtual ~ cImagePlayer(); + bool IsConvertRunning() const { return m_bConvertRunning; }; + bool NextImage(int Step); + bool PrevImage(int Step); + bool GotoImage(unsigned int nNewPictureIndex); + + /** Deliver the filename from the current number of viewed Image */ + const char* FileName(void) const; + + bool Convert(const char *szChange); + bool ConvertZoom(const char *szChange, int nZoomFaktor, + int nLeftPos, int nTopPos); + bool ConvertJump(int Step); + + /** Returns the current and total frame index, optionally snapped to the + nearest I-frame.*/ + virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame); + /** ThreadSafe Method to show messages from Worker thread*/ + void ErrorMsg(); +}; + +#endif //__DVB_IMAGE_H diff --git a/scripts/imageplugin.sh b/scripts/imageplugin.sh new file mode 100755 index 0000000..2afd734 --- /dev/null +++ b/scripts/imageplugin.sh @@ -0,0 +1,160 @@ +#!/bin/bash +# script for vdr-imageplugin to convert the selected image to pnm-image +# needs : netpbm-progs > anytopnm pnmscale pnmfile pnmcut pnmflip +# +# History: +# 2004-08-12 Initalrelease, Andreas Brachold +# base on prior work for convert.sh +# by Onno Kreuzinger +# Andreas Holzhammer and +# +################################################################################ +# Userconfig: +################################################################################ +# if your install external software like netpbm outside /bin:/usr/bin, adjust folder +PATH=/usr/local/bin:$PATH +# Set to "yes" to speedup reviewed image +TMPCACHE=no +# Set to "no" if this script work and your don't need success messages +VERBOSE=yes +# Set to "yes" if this script don't work, only usable if your self execute the script from shell +DEBUG=no + +################################################################################ +# and now the script +################################################################################ +[ "z$DEBUG" = "zyes" ] && set -xv +SCRIPTNAME=$(basename "$0") +{ +[ "z$VERBOSE" = "zyes" ] && echo "called '$SCRIPTNAME $*'" + +if [ $# -lt 7 ] ; then + echo "Usage: $SCRIPTNAME infile outfile WIDTH HEIGHT ZOOMFACTOR LEFTPOS TOPPOS [FLIPCMD]" 1>&2 + echo " e.g.: $SCRIPTNAME in.png out.pnm 720 576 0 0 0" 1>&2 + echo " or .: $SCRIPTNAME in.png out.pnm 720 576 3 360 360 left" 1>&2 + echo "" 1>&2 + echo "WIDTH - Width of TVScreen (720)" 1>&2 + echo "HEIGHT - Height of TVScreen (480..576)" 1>&2 + echo "ZOOMFACTOR - Zoomfactor (0....10)" 1>&2 + echo "LEFTPOS - Offset from left on Zoommode (0......)" 1>&2 + echo "TOPPOS - Offset from top on Zoommode (0......)" 1>&2 + echo "FLIPCMD - optional should image flip (left,right,original)" 1>&2 + exit 1 +fi + + # Defaultvalue, overwrite with env from plugin + ASPECT_RATIO="${ASPECT_RATIO:-"4:3"}" + + # check requirement external programs + REQUIREMENTS="anytopnm pnmscale pnmfile pnmcut pnmflip" + for i in $REQUIREMENTS + do + type "$i" > /dev/null 2>&1 + [ $? -ne 0 ] && echo -e "$SCRIPTNAME: Error ! External required program: \"$i\" not found !\n Please adjust PATH or install it inside follow Folder \"$PATH\" \n" && exit 1 + done + + INFILE="$1" + OUTFILE="$2" + OUT_DISPLAY_X=$3 + OUT_DISPLAY_Y=$4 + ZOOMFACTOR=$5 + LEFTPOS=$6 + TOPPOS=$7 + FLIPCMD="$8" + + OUTDIR=$(dirname "$OUTFILE") + [ ! -d "$OUTDIR" ] && mkdir -p "$OUTDIR" + + TMPFILE="$OUTFILE.tmp" + PARFILE="$OUTFILE.par" + + # remove precreated files if called with flip "left","right" or "original" + [ -s "$OUTFILE" -a "$FLIPCMD" != "" ] && rm -f "$OUTFILE" + [ -s "$TMPFILE" -a "$FLIPCMD" != "" ] && rm -f "$TMPFILE" + + if [ -s "$OUTFILE" ] ; then + [ "z$VERBOSE" = "zyes" ] && echo "Success! Convert not required, $OUTFILE exists already!" + exit 0 + else + + # Convert Image to PNM File + [ ! -s "$TMPFILE" ] && anytopnm "$INFILE" > "$TMPFILE" + # if success + if [ -s "$TMPFILE" ] ; then + + # Get image resolution + RES=`echo $( pnmfile < "$TMPFILE" -)` # checked with netpbm 10.0, + # Parse pnmfile output "-: PPM raw, 768 by 576 maxval 255" => 768 x 576 + X_RES=$(echo -e "$RES"| cut -d " " -f 4) + Y_RES=$(echo -e "$RES"| cut -d " " -f 6) + + # set flip command + case "$FLIPCMD" in + right ) + FLIP="pnmflip -rotate270" + SWAPRES=$X_RES;X_RES=$Y_RES;Y_RES=$SWAPRES + ;; + left ) + FLIP="pnmflip -rotate90"; + SWAPRES=$X_RES;X_RES=$Y_RES;Y_RES=$SWAPRES + ;; + *) + FLIP="cat"; + ;; + esac + # Save config for plugin as readable file + echo "$X_RES" "$Y_RES" "$FLIPCMD" > "$PARFILE" + + # define aspect ratio depends plugin setup + if [ $ASPECT_RATIO = "16:9" ] ; then + SCALE_MIN_ASP=163 + SCALE_MAX_ASP=178 + else + SCALE_MIN_ASP=125 + SCALE_MAX_ASP=133 + fi + + # if zoom image, zoom it with factor + if [ "$ZOOMFACTOR" -gt 0 ] ; then + + ZOOM_X=$(($X_RES*$ZOOMFACTOR)) + ZOOM_Y=$(($Y_RES*$ZOOMFACTOR)) + + $FLIP < "$TMPFILE" | pnmscalefixed -xysize $ZOOM_X $ZOOM_Y | \ + pnmcut -pad -left $LEFTPOS -top $TOPPOS -width $OUT_DISPLAY_X -height $OUT_DISPLAY_Y \ + > "$OUTFILE" + + # else scale image to TV Screensize + else + + if [ "$((${X_RES}00 / ${Y_RES}))" -lt $SCALE_MIN_ASP ] ; then + OUT_DISPLAY_X=$((${OUT_DISPLAY_Y}000 / $Y_RES * $X_RES / 1000)) + elif [ "$((${X_RES}00 / ${Y_RES}))" -gt $SCALE_MAX_ASP ] ; then + OUT_DISPLAY_Y=$((${OUT_DISPLAY_X}000 / $X_RES * $Y_RES / 1000)) + fi + + $FLIP < "$TMPFILE" | \ + pnmscalefixed -xysize $OUT_DISPLAY_X $OUT_DISPLAY_Y \ + > "$OUTFILE" + + fi + # if'nt tmpfile use for next view, remove tmp file + [ "z$TMPCACHE" = "zno" ] && rm -f "$TMPFILE" + fi + fi + + if [ -s "$OUTFILE" ] ; then + [ "z$VERBOSE" = "zyes" ] && echo "Success! Stopped with created $OUTFILE" + exit 0 # Creation seem success, tell it with 'exit 0' to plugin + fi + [ "z$VERBOSE" = "zyes" ] && echo "Error! Stopped without found created $OUTFILE, converting should failed !" + exit 1 # Hmm, created is failed tell it with 'exit 1' to plugin + + +###### >>>>>>> !!! Only one of the follow lines are allowed (begins always with 2>&1 ) !!! +### Dump any message to syslog to see use cat /var/log/messages | grep imageplugin +} 2>&1 | logger -s -t "$SCRIPTNAME" +### If your wish don't any message logging +# 2>&1 > /dev/null +### If your wish old style message logging +# 2>&1 > /tmp/image/convert.log diff --git a/scripts/mount.sh b/scripts/mount.sh new file mode 100755 index 0000000..65cb6e0 --- /dev/null +++ b/scripts/mount.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# This script is called from VDR to mount/unmount/eject +# the sources for MP3 play and/or image sources, +# +# argument 1: wanted action, one of mount,unmount,eject,status +# argument 2: mountpoint to act on +# +# mount,unmount,eject must return 0 if succeeded, 1 if failed +# status must return 0 if device is mounted, 1 if not +# +# ok -> changed mount to not abort, if eject -t did fail, e.g. for a usb storage device + +action="$1" +path="$2" + +case "$action" in +mount) + eject -t "$path" &>/dev/null # close the tray, but not do/print anything if that fails + mount "$path" || exit 1 # mount it + ;; +unmount) + umount "$path" || exit 1 # unmount it + ;; +eject) + eject "$path" || exit 1 # eject disk + ;; +status) + cat /proc/mounts | grep -q "$path" # check if mounted + if [ $? -ne 0 ]; then # not mounted ... + exit 1 + fi +esac + +exit 0 diff --git a/setup-image.c b/setup-image.c new file mode 100644 index 0000000..8cf05d0 --- /dev/null +++ b/setup-image.c @@ -0,0 +1,145 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold + * (C) 2003 Kai Tobias Burwieck + * + * based on MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + * + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include "setup-image.h" +#include "i18n.h" + +cImageSetup ImageSetup; + +const int cImageSetup::m_cSSMin = 2; +const int cImageSetup::m_cSSMax = 300; + +// --- cImageSetup ----------------------------------------------------------- + +cImageSetup::cImageSetup(void) + +{ + SlideShow = 0; + SSsec = 10; + strncpy(TempDir, "/tmp/image", sizeof(TempDir)); +#if VDRVERSNUM < 10307 + ShowDate = 1; +#endif + AutoRepeat = 0; + ShowNumbers = 1; +#if VDRVERSNUM >= 10308 + m_bLiveAudio = 0; +#endif + m_bHousekeeping = 1; +} + +bool cImageSetup::SetupParse(const char *szName, const char *szValue) +{ + if(!strcasecmp(szName, "SlideShow")) SlideShow = atoi(szValue); + else if(!strcasecmp(szName, "SSsec")) + { + SSsec = atoi(szValue); + if(SSsec < m_cSSMin) SSsec = cImageSetup::m_cSSMin; + if(SSsec > m_cSSMax) SSsec = cImageSetup::m_cSSMax; + } + else if(!strcasecmp(szName, "TempDir")) strn0cpy(TempDir,szValue,sizeof(TempDir)); +#if VDRVERSNUM < 10307 + else if(!strcasecmp(szName, "ShowDate")) ShowDate = atoi(szValue); +#endif + else if(!strcasecmp(szName, "AutoRepeat")) AutoRepeat = atoi(szValue); + else if(!strcasecmp(szName, "ShowNumbers")) ShowNumbers = atoi(szValue); +#if VDRVERSNUM >= 10308 + else if(!strcasecmp(szName, "LiveAudio")) m_bLiveAudio = atoi(szValue); +#endif + else if(!strcasecmp(szName, "Housekeeping")) m_bHousekeeping = atoi(szValue); + else return false; + return true; +} + +// --- cMenuSetupImage -------------------------------------------------------- +void cMenuSetupImage::Store(void) +{ + ImageSetup = m_tmpSetup; + SetupStore("SlideShow", ImageSetup.SlideShow); + SetupStore("SSsec", ImageSetup.SSsec); + SetupStore("TempDir", ImageSetup.TempDir); +#if VDRVERSNUM < 10307 + SetupStore("ShowDate", ImageSetup.ShowDate); +#endif + SetupStore("AutoRepeat", ImageSetup.AutoRepeat); + SetupStore("ShowNumbers", ImageSetup.ShowNumbers); +#if VDRVERSNUM >= 10308 + SetupStore("LiveAudio", ImageSetup.m_bLiveAudio); +#endif + SetupStore("Housekeeping", ImageSetup.m_bHousekeeping); +} + +cMenuSetupImage::cMenuSetupImage(void) +: m_tmpSetup(ImageSetup) +{ + SetSection(tr("Image")); + Add(new cMenuEditBoolItem(tr("SlideShow ?"), &m_tmpSetup.SlideShow, tr("no"), tr("yes"))); + Add(new cMenuEditIntItem (tr("Slide duration (sec)"), &m_tmpSetup.SSsec, cImageSetup::m_cSSMin, cImageSetup::m_cSSMax)); + Add(new cMenuEditBoolItem(tr("Repeat SlideShow"), &m_tmpSetup.AutoRepeat, tr("no"), tr("yes"))); +#if VDRVERSNUM < 10307 + Add(new cMenuEditBoolItem(tr("Show Filedate on OSD"), &m_tmpSetup.ShowDate, tr("no"), tr("yes"))); +#endif + Add(new cMenuEditBoolItem(tr("Show Numbers on index image"), &m_tmpSetup.ShowNumbers, tr("no"), tr("yes"))); +#if VDRVERSNUM >= 10308 + Add(new cMenuEditBoolItem(tr("Live Audio from primary Device"), &m_tmpSetup.m_bLiveAudio, tr("no"), tr("yes"))); +#endif + Add(new cMenuEditStrItem (tr("Directory with temporary files"), m_tmpSetup.TempDir,sizeof(m_tmpSetup.TempDir), "abcdefghijklmopqrstuvwxyz/-")); + Add(new cMenuEditBoolItem(tr("Remove temporary files"), &m_tmpSetup.m_bHousekeeping, tr("no"), tr("yes"))); +} + + + + + +void cImageSetup::SetEnv(void) const +{ + unsigned int i; + struct cImageCommandEnv { + const char* szEnv; + const char* szValue; + } nEnvironTable [] = + { + {"ASPECT_RATIO",Setup.VideoFormat?"16:9":"4:3"}, // Get from DVB-Setup + {"CONVERT_TEMPDIR",TempDir}, + }; + + for(i=0;i < sizeof(nEnvironTable)/sizeof(*nEnvironTable);++i) + { + if(nEnvironTable[i].szValue) { + if(0!= setenv(nEnvironTable[i].szEnv, nEnvironTable[i].szValue,1)) + esyslog("imageplugin: can't setenv '%s = %s'", nEnvironTable[i].szEnv, nEnvironTable[i].szValue); + } + else { + if(0!= unsetenv(nEnvironTable[i].szEnv)) + esyslog("imageplugin: can't unsetenv '%s'", nEnvironTable[i].szEnv); + } + } +} diff --git a/setup-image.h b/setup-image.h new file mode 100644 index 0000000..b0a1f58 --- /dev/null +++ b/setup-image.h @@ -0,0 +1,73 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2003 Kai Tobias Burwieck + * + * This code 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 2 + * of the License, or (at your option) any later version. + * + * This code 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef ___SETUP_IMAGE_H +#define ___SETUP_IMAGE_H + +extern const char *g_szConvertScript; + +#include + +class cImageSetup +{ +public: + int SlideShow; + int SSsec; + char TempDir[260]; +#if VDRVERSNUM < 10307 + int ShowDate; +#endif + int AutoRepeat; + int ShowNumbers; +#if VDRVERSNUM >= 10308 + int m_bLiveAudio; +#endif + int m_bHousekeeping; + + /** Minimum Value for Slideshow */ + static const int m_cSSMin; + /** Maximum Value for Slideshow */ + static const int m_cSSMax; + +public: + cImageSetup(void); + void SetEnv(void) const; + + bool SetupParse(const char *szName, const char *szValue); +}; + + +// ---------------------------------------------------------------- + +class cMenuSetupImage + :public cMenuSetupPage +{ + cImageSetup m_tmpSetup; +protected: + virtual void Store(void); +public: + cMenuSetupImage(void); +}; + + +extern cImageSetup ImageSetup; + +#endif //___SETUP_IMAGE_H -- cgit v1.2.3