diff options
author | Andreas Brachold <vdr07@deltab.de> | 2005-07-19 15:09:05 +0000 |
---|---|---|
committer | Andreas Brachold <vdr07@deltab.de> | 2005-07-19 15:09:05 +0000 |
commit | f897f2aa7055c493db6391c50c8d19da970078e8 (patch) | |
tree | d13a515b24c149d7da4e9828cc9e9c73d4916f00 | |
download | vdr-plugin-image-f897f2aa7055c493db6391c50c8d19da970078e8.tar.gz vdr-plugin-image-f897f2aa7055c493db6391c50c8d19da970078e8.tar.bz2 |
Initial import with release 0.2.3
61 files changed, 12036 insertions, 0 deletions
@@ -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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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. + + <signature of Ty Coon>, 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. @@ -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 <dvgn-at-libero-dot-it> +- 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 <steffx-at-vdr-portal-de> 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 <steffx-at-vdr-portal-de> 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 <kreuzinger.biz>) +- 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 @@ -0,0 +1,148 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Originaly written by: Kai Tobias Burwieck <kai-at-burwieck.net> + "Interpohl" <interpohl-at-vdr-portal.de> + Onno Kreuzinger <o.kreuzinger-at-kreuzinger.biz> + Andreas Brachold <vdr04-at-deltab.de> + +Former project's homepage: http://www.burwieck.net/vdr (dead?) + http://vdr-image.kreuzinger.biz> + +Maintainer: Andreas Brachold <vdr04-at-deltab.de> + +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 : +<path>;<name in vdr menu>;<0/1>;<pattern to filter file display> + +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 <kai-at-burwieck.net> + "Interpohl" <vdr-portal.de> + Onno Kreuzinger <o.kreuzinger-at-kreuzinger.biz> + Andreas Brachold <vdr04-at-deltab.de> + +Former project's homepage: http://www.burwieck.net/vdr (dead?) + http://vdr-image.kreuzinger.biz> + +Maintainer: Andreas Brachold <vdr04-at-deltab.de> + +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: +<Pfad>;<Name im vdr Menu>;<0/1>;<Suchmuster für Dateien die angezeigt werden sollen> + +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. @@ -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 <vdr04-at-deltab.de> + * + * 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 <malloc.h> +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> + +#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 <vdr04-at-deltab.de> + * + * 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 <vdr/tools.h> + +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] <file|folder>" +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 <vdr04 -at- deltab.de> +* based on (C) 2003 Kai Tobias Burwieck <kai -at- burwieck.net> +* +* 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 <time.h> + +#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 <vdr/status.h> +#include <vdr/tools.h> +#include <vdr/plugin.h> + + +#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 <vdr04 -at- deltab.de> + * based on (C) 2003 Kai Tobias Burwieck <kai -at- burwieck.net> + * + * 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 <vdr/player.h> +#if VDRVERSNUM >= 10307 +#include <vdr/osdbase.h> +#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 <kai@burwieck.net> + * + * 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 <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#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 <kai@burwieck.net> + * + * 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 <vdr/tools.h> + +// ---------------------------------------------------------------- + +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 @@ -0,0 +1,508 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net> + * + * based on MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt <huels@iname.com> + * + * 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 <ctype.h> +#include <dirent.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "data.h" +#include "data-image.h" +#include "list.h" + +#include <vdr/tools.h> + +// ---------------------------------------------------------------- + +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<cFileExt> +{ +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; +} @@ -0,0 +1,128 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net> +* + * based on MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt <huels@iname.com> + * + * + * 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 <vdr/tools.h> +#include <vdr/config.h> + +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<cDirItem> { + 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> { + 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: <name>[?];<command> +# +# <name> = descriptor displayed in VDR +# [?] = confirmation request, before execute command (optionally) +# <command>= 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>;<name>;<mount>;<filter> +# +# <path> = Path, where to finde images or image directories +# <name> = descriptor displayed in VDR +# <mount> = 0 - if no mounting should be done +# 1 - if <path> needs to be mounted first. +# (Dont forget to setup fstab !!!) +# <filter> = 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 @@ -0,0 +1,625 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold <vdr04-at-deltab.de> + * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net> + * + * 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 <vdr/config.h> +#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 } +}; @@ -0,0 +1,30 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold <vdr04-at-deltab.de> + * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net> + * + * 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 <vdr/i18n.h> + +extern const tI18nPhrase Phrases[]; + +#endif //___I18N_H @@ -0,0 +1,129 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net> + * (C) 2004 "Interpohl" <interpohl@vdr-portal.de> + * (C) 2004 A. Brachold <vdr04-at-deltab.de> + * (C) 2004 O. Kreuzinger <Onno@Kreuzinger.biz> + * (C) 2004 A. Holzhammer for the massive script updates + * + * based on mp3/mplayer plguin by Stefan Hülswitt <huels@iname.com> + * + * 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 <getopt.h> +#include <vdr/plugin.h> + + +#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! @@ -0,0 +1,67 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold <vdr04-at-deltab.de> + * + * 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 <vdr/config.h> +#if VDRVERSNUM < 10307 +#include <vdr/osd.h> +#else +#include <vdr/osdbase.h> +#include <vdr/skins.h> +#endif +#include <string.h> + +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 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" +"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> +<!-- Created with Sodipodi ("http://www.sodipodi.com/") --> +<svg + width="704pt" + height="576pt" + viewBox="0 0 256 256" + style="overflow:visible;enable-background:new 0 0 256 256" + xml:space="preserve" + id="svg3469" + sodipodi:version="0.33" + sodipodi:docname="Image-Error.svg" + sodipodi:docbase="." + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xml="http://www.w3.org/XML/1998/namespace" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs + id="defs3579"> + <linearGradient + id="XMLID_1_" + gradientUnits="userSpaceOnUse" + x1="128.9995" + y1="11" + x2="128.9995" + y2="245.0005"> + <stop + offset="0" + style="stop-color:#494949" + id="stop3476" /> + + + + + <stop + offset="1" + style="stop-color:#000000" + id="stop3477" /> + + + + + </linearGradient> + + + + + <linearGradient + id="XMLID_2_" + gradientUnits="userSpaceOnUse" + x1="29.0532" + y1="29.0532" + x2="226.9471" + y2="226.9471"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop3484" /> + + + + + <stop + offset="1" + style="stop-color:#DADADA" + id="stop3485" /> + + + + + </linearGradient> + + + + + </defs> + + + + <sodipodi:namedview + id="base"> + <sodipodi:guide + orientation="horizontal" + position="287.129913" + id="guide2108" /> + + <sodipodi:guide + orientation="vertical" + position="29.7764359" + id="guide2109" /> + + <sodipodi:guide + orientation="vertical" + position="676.350464" + id="guide2110" /> + + <sodipodi:guide + orientation="horizontal" + position="550.864075" + id="guide2111" /> + + <sodipodi:guide + orientation="horizontal" + position="31.9033241" + id="guide2112" /> + +</sodipodi:namedview> + + + + <path + style="font-size:12;fill:none;" + d="M 222.4303 159.0556 L 118.0510 159.0556 L 118.0510 34.51719 L 222.4303 34.51719 L 222.4303 159.0556 z " + id="path3490" /> + + + + <g + id="g3491" + style="font-size:12;stroke:#000000;" + transform="matrix(0.583057,0,0,0.695663,106.1162,4.982203)"> + <path + style="opacity:0.2;stroke:none;" + d="M72.626,58.727c-3.336,0-6.049,2.713-6.049,6.048v132.323c0,3.335,2.713,6.048,6.049,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V64.774c0-3.335-2.714-6.048-6.049-6.048H72.626z" + id="path3492" /> + + + + + <path + style="opacity:0.2;stroke:none;" + d="M71.451,56.378c-3.335,0-6.048,2.713-6.048,6.049v132.322c0,3.335,2.713,6.048,6.048,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V62.427c0-3.336-2.714-6.049-6.049-6.049H71.451z" + id="path3493" /> + + + + + <path + style="opacity:0.2;stroke:none;" + d="M69.103,55.203c-3.335,0-6.049,2.713-6.049,6.048v132.323c0,3.335,2.714,6.048,6.049,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V61.251c0-3.335-2.714-6.048-6.049-6.048H69.103z" + id="path3494" /> + + + + + <linearGradient + id="XMLID_3_" + gradientUnits="userSpaceOnUse" + x1="61.8799" + y1="125.064" + x2="189.4219" + y2="125.064"> + <stop + offset="0" + style="stop-color:#616161" + id="stop3496" /> + + + + + <stop + offset="1" + style="stop-color:#464646" + id="stop3497" /> + + + + + </linearGradient> + + + + + <path + style="fill:url(#XMLID_3_);stroke:none;" + d="M67.928,52.854c-3.335,0-6.048,2.713-6.048,6.049v132.322c0,3.335,2.713,6.048,6.048,6.048h115.444 c3.336,0,6.05-2.713,6.05-6.048V58.903c0-3.336-2.714-6.049-6.05-6.049H67.928z" + id="path3501" /> + + + + + <path + style="fill:#FFFFFF;stroke:none;" + d="M184.725,191.226c0,0.747-0.606,1.351-1.353,1.351H67.928c-0.745,0-1.351-0.604-1.351-1.351V58.903 c0-0.746,0.605-1.352,1.351-1.352h115.444c0.746,0,1.353,0.605,1.353,1.352V191.226z" + id="path3502" /> + + + + + <linearGradient + id="XMLID_4_" + gradientUnits="userSpaceOnUse" + x1="233.4971" + y1="232.6162" + x2="51.0675" + y2="50.1867"> + <stop + offset="0" + style="stop-color:#CECEDB" + id="stop3504" /> + + + + + <stop + offset="0.691" + style="stop-color:#E9E9EF" + id="stop3505" /> + + + + + <stop + offset="1" + style="stop-color:#FFFFFF" + id="stop3506" /> + + + + + </linearGradient> + + + + + <path + style="fill:url(#XMLID_4_);stroke:none;" + d="M69.954,60.635c0,4.134,0,123.846,0,127.978c4.104,0,106.998,0,111.101,0c0-4.132,0-123.844,0-127.978 C176.952,60.635,74.058,60.635,69.954,60.635z" + id="path3512" /> + + + + + <linearGradient + id="XMLID_5_" + gradientUnits="userSpaceOnUse" + x1="-654.9922" + y1="23.1128" + x2="-520.0903" + y2="23.1128" + gradientTransform="matrix(0 1 -1 0 148.7637 686.498)"> + <stop + offset="0" + style="stop-color:#BDBDCF" + id="stop3514" /> + + + + + <stop + offset="0.1923" + style="stop-color:#CCCCDA" + id="stop3515" /> + + + + + <stop + offset="0.7201" + style="stop-color:#F1F1F5" + id="stop3516" /> + + + + + <stop + offset="1" + style="stop-color:#FFFFFF" + id="stop3517" /> + + + + + </linearGradient> + + + + + <path + style="fill:url(#XMLID_5_);stroke:none;" + d="M176.563,67.165c-3.289,0-98.536,0-101.823,0c0,3.266,0,85.131,0,88.396c3.287,0,98.534,0,101.823,0 C176.563,152.296,176.563,70.431,176.563,67.165z" + id="path3521" /> + + + + + <path + style="stroke:none;" + d="M78.46,70.886c0,6.529,0,74.425,0,80.955c6.646,0,87.734,0,94.381,0c0-6.53,0-74.426,0-80.955 C166.194,70.886,85.106,70.886,78.46,70.886z" + id="path3522" /> + + + + + <radialGradient + id="XMLID_6_" + cx="109.7783" + cy="116.7822" + r="66.1491" + fx="109.7783" + fy="116.7822" + gradientUnits="userSpaceOnUse"> + <stop + offset="0" + style="stop-color:#B1E1FF" + id="stop3524" /> + + + + + <stop + offset="0.1441" + style="stop-color:#AEDFFF" + id="stop3525" /> + + + + + <stop + offset="0.2661" + style="stop-color:#A3D9FF" + id="stop3526" /> + + + + + <stop + offset="0.38" + style="stop-color:#92CFFF" + id="stop3527" /> + + + + + <stop + offset="0.489" + style="stop-color:#79C1FF" + id="stop3528" /> + + + + + <stop + offset="0.5937" + style="stop-color:#5AAEFF" + id="stop3529" /> + + + + + <stop + offset="0.6011" + style="stop-color:#57ADFF" + id="stop3530" /> + + + + + <stop + offset="1" + style="stop-color:#0035ED" + id="stop3531" /> + + + + + </radialGradient> + + + + + <path + style="fill:url(#XMLID_6_);stroke:none;" + d="M80.94,73.367c0,4.515,0,71.477,0,75.993c4.577,0,84.844,0,89.42,0c0-4.517,0-71.479,0-75.993 C165.784,73.367,85.518,73.367,80.94,73.367z" + id="path3537" /> + + + + + <linearGradient + id="XMLID_7_" + gradientUnits="userSpaceOnUse" + x1="100.4385" + y1="122.5811" + x2="118.1257" + y2="104.8938"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop3539" /> + + + + + <stop + offset="1" + style="stop-color:#E9E9EF" + id="stop3540" /> + + + + + </linearGradient> + + + + + <path + style="fill:url(#XMLID_7_);stroke:none;" + d="M96.774,113.736c0,6.907,5.6,12.508,12.507,12.508s12.506-5.601,12.506-12.508 s-5.599-12.507-12.506-12.507S96.774,106.829,96.774,113.736z" + id="path3544" /> + + + + + <path + style="stroke:none;" + d="M78.46,137.866c0,1.127,0,12.845,0,13.975c6.646,0,87.734,0,94.381,0c0-1.13,0-12.848,0-13.975 C166.194,137.866,85.106,137.866,78.46,137.866z" + id="path3545" /> + + + + + <path + style="stroke:none;" + d="M100.665,117.415h-8.486v25.92h8.486V117.415z" + id="path3546" /> + + + + + <path + style="stroke:none;" + d="M108.007,127.737h-8.488v15.828h8.488V127.737z" + id="path3547" /> + + + + + <path + style="stroke:none;" + d="M118.099,144.023h-11.01v-10.78h11.01V144.023z" + id="path3548" /> + + + + + <path + style="stroke:none;" + d="M123.374,119.938h-6.65v19.499h6.65V119.938z" + id="path3549" /> + + + + + <path + style="stroke:none;" + d="M139.892,144.023h-17.205v-15.599h17.205V144.023z" + id="path3550" /> + + + + + <path + style="stroke:none;" + d="M145.396,108.927h-7.34v32.116h7.34V108.927z" + id="path3551" /> + + + + + <path + style="stroke:none;" + d="M80.708,144.023h11.699v-19.817H80.708V144.023z" + id="path3552" /> + + + + + <path + style="stroke:none;" + d="M154.571,143.104h-11.01v-23.396h11.01V143.104z" + id="path3553" /> + + + + + <path + style="stroke:none;" + d="M170.683,145.87h-12.664v-17.662h12.664V145.87z" + id="path3554" /> + + + + + <path + style="stroke:none;" + d="M169.024,142.416h-7.57v-25.003h7.57V142.416z" + id="path3555" /> + + + + + <path + style="stroke:none;" + d="M130.486,138.059h-7.569v-25.005h7.569V138.059z" + id="path3556" /> + + + + + <path + style="stroke:none;" + d="M80.938,144.482h88.086v-7.8H80.938V144.482z" + id="path3557" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M94.063,121.743h2.016v-2.016h-2.016V121.743z" + id="path3558" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M94.063,126.23h2.016v-2.016h-2.016V126.23z" + id="path3559" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M94.063,135.205h2.016v-2.016h-2.016V135.205z" + id="path3560" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M125.874,122.173h2.015v-2.016h-2.015V122.173z" + id="path3561" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M125.874,131.147h2.015v-2.016h-2.015V131.147z" + id="path3562" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M119.207,124.662h2.016v-2.015h-2.016V124.662z" + id="path3563" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M119.207,129.149h2.016v-2.016h-2.016V129.149z" + id="path3564" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M119.207,133.638h2.016v-2.016h-2.016V133.638z" + id="path3565" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M102.489,132.554h2.015v-2.018h-2.015V132.554z" + id="path3566" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M102.489,137.038h2.015v-2.015h-2.015V137.038z" + id="path3567" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M82.507,128.463h2.015v-2.016h-2.015V128.463z" + id="path3568" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M82.507,132.95h2.015v-2.017h-2.015V132.95z" + id="path3569" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M149.356,128.747h2.015v-2.016h-2.015V128.747z" + id="path3570" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M140.674,114.493h2.015v-2.017h-2.015V114.493z" + id="path3571" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M140.674,118.979h2.015v-2.016h-2.015V118.979z" + id="path3572" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M140.674,123.468h2.015v-2.016h-2.015V123.468z" + id="path3573" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M140.674,127.954h2.015v-2.016h-2.015V127.954z" + id="path3574" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M164.085,122.089h2.016v-2.016h-2.016V122.089z" + id="path3575" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M164.085,131.065h2.016v-2.016h-2.016V131.065z" + id="path3576" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M129.602,134.712v2.016h2.016v-2.016H129.602z" + id="path3577" /> + + + + + </g> + + + + <path + style="font-size:12;fill:none;" + d="M 226.0942 146.8087 L 135.6992 209.0778 L 83.50963 101.2246 L 173.9046 38.95533 L 226.0942 146.8087 z " + id="path1323" /> + + + + <g + id="g1324" + style="font-size:12;stroke:#000000;" + transform="matrix(0.504941,-0.347832,0.291527,0.602462,63.36050,88.66474)"> + <path + style="opacity:0.2;stroke:none;" + d="M72.626,58.727c-3.336,0-6.049,2.713-6.049,6.048v132.323c0,3.335,2.713,6.048,6.049,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V64.774c0-3.335-2.714-6.048-6.049-6.048H72.626z" + id="path1325" /> + + + + + <path + style="opacity:0.2;stroke:none;" + d="M71.451,56.378c-3.335,0-6.048,2.713-6.048,6.049v132.322c0,3.335,2.713,6.048,6.048,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V62.427c0-3.336-2.714-6.049-6.049-6.049H71.451z" + id="path1326" /> + + + + + <path + style="opacity:0.2;stroke:none;" + d="M69.103,55.203c-3.335,0-6.049,2.713-6.049,6.048v132.323c0,3.335,2.714,6.048,6.049,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V61.251c0-3.335-2.714-6.048-6.049-6.048H69.103z" + id="path1327" /> + + + + + <linearGradient + id="linearGradient1328" + gradientUnits="userSpaceOnUse" + x1="61.8799" + y1="125.064" + x2="189.4219" + y2="125.064"> + <stop + offset="0" + style="stop-color:#616161" + id="stop1329" /> + + + + + <stop + offset="1" + style="stop-color:#464646" + id="stop1330" /> + + + + + </linearGradient> + + + + + <path + style="fill:url(#XMLID_3_);stroke:none;" + d="M67.928,52.854c-3.335,0-6.048,2.713-6.048,6.049v132.322c0,3.335,2.713,6.048,6.048,6.048h115.444 c3.336,0,6.05-2.713,6.05-6.048V58.903c0-3.336-2.714-6.049-6.05-6.049H67.928z" + id="path1331" /> + + + + + <path + style="fill:#FFFFFF;stroke:none;" + d="M184.725,191.226c0,0.747-0.606,1.351-1.353,1.351H67.928c-0.745,0-1.351-0.604-1.351-1.351V58.903 c0-0.746,0.605-1.352,1.351-1.352h115.444c0.746,0,1.353,0.605,1.353,1.352V191.226z" + id="path1332" /> + + + + + <linearGradient + id="linearGradient1333" + gradientUnits="userSpaceOnUse" + x1="233.4971" + y1="232.6162" + x2="51.0675" + y2="50.1867"> + <stop + offset="0" + style="stop-color:#CECEDB" + id="stop1334" /> + + + + + <stop + offset="0.691" + style="stop-color:#E9E9EF" + id="stop1335" /> + + + + + <stop + offset="1" + style="stop-color:#FFFFFF" + id="stop1336" /> + + + + + </linearGradient> + + + + + <path + style="fill:url(#XMLID_4_);stroke:none;" + d="M69.954,60.635c0,4.134,0,123.846,0,127.978c4.104,0,106.998,0,111.101,0c0-4.132,0-123.844,0-127.978 C176.952,60.635,74.058,60.635,69.954,60.635z" + id="path1337" /> + + + + + <linearGradient + id="linearGradient1338" + gradientUnits="userSpaceOnUse" + x1="-654.9922" + y1="23.1128" + x2="-520.0903" + y2="23.1128" + gradientTransform="matrix(0 1 -1 0 148.7637 686.498)"> + <stop + offset="0" + style="stop-color:#BDBDCF" + id="stop1339" /> + + + + + <stop + offset="0.1923" + style="stop-color:#CCCCDA" + id="stop1340" /> + + + + + <stop + offset="0.7201" + style="stop-color:#F1F1F5" + id="stop1341" /> + + + + + <stop + offset="1" + style="stop-color:#FFFFFF" + id="stop1342" /> + + + + + </linearGradient> + + + + + <path + style="fill:url(#XMLID_5_);stroke:none;" + d="M176.563,67.165c-3.289,0-98.536,0-101.823,0c0,3.266,0,85.131,0,88.396c3.287,0,98.534,0,101.823,0 C176.563,152.296,176.563,70.431,176.563,67.165z" + id="path1343" /> + + + + + <path + style="stroke:none;" + d="M78.46,70.886c0,6.529,0,74.425,0,80.955c6.646,0,87.734,0,94.381,0c0-6.53,0-74.426,0-80.955 C166.194,70.886,85.106,70.886,78.46,70.886z" + id="path1344" /> + + + + + <radialGradient + id="radialGradient1345" + cx="109.7783" + cy="116.7822" + r="66.1491" + fx="109.7783" + fy="116.7822" + gradientUnits="userSpaceOnUse"> + <stop + offset="0" + style="stop-color:#B1E1FF" + id="stop1346" /> + + + + + <stop + offset="0.1441" + style="stop-color:#AEDFFF" + id="stop1347" /> + + + + + <stop + offset="0.2661" + style="stop-color:#A3D9FF" + id="stop1348" /> + + + + + <stop + offset="0.38" + style="stop-color:#92CFFF" + id="stop1349" /> + + + + + <stop + offset="0.489" + style="stop-color:#79C1FF" + id="stop1350" /> + + + + + <stop + offset="0.5937" + style="stop-color:#5AAEFF" + id="stop1351" /> + + + + + <stop + offset="0.6011" + style="stop-color:#57ADFF" + id="stop1352" /> + + + + + <stop + offset="1" + style="stop-color:#0035ED" + id="stop1353" /> + + + + + </radialGradient> + + + + + <path + style="fill:url(#XMLID_6_);stroke:none;" + d="M80.94,73.367c0,4.515,0,71.477,0,75.993c4.577,0,84.844,0,89.42,0c0-4.517,0-71.479,0-75.993 C165.784,73.367,85.518,73.367,80.94,73.367z" + id="path1354" /> + + + + + <linearGradient + id="linearGradient1355" + gradientUnits="userSpaceOnUse" + x1="100.4385" + y1="122.5811" + x2="118.1257" + y2="104.8938"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop1356" /> + + + + + <stop + offset="1" + style="stop-color:#E9E9EF" + id="stop1357" /> + + + + + </linearGradient> + + + + + <path + style="fill:url(#XMLID_7_);stroke:none;" + d="M96.774,113.736c0,6.907,5.6,12.508,12.507,12.508s12.506-5.601,12.506-12.508 s-5.599-12.507-12.506-12.507S96.774,106.829,96.774,113.736z" + id="path1358" /> + + + + + <path + style="stroke:none;" + d="M78.46,137.866c0,1.127,0,12.845,0,13.975c6.646,0,87.734,0,94.381,0c0-1.13,0-12.848,0-13.975 C166.194,137.866,85.106,137.866,78.46,137.866z" + id="path1359" /> + + + + + <path + style="stroke:none;" + d="M100.665,117.415h-8.486v25.92h8.486V117.415z" + id="path1360" /> + + + + + <path + style="stroke:none;" + d="M108.007,127.737h-8.488v15.828h8.488V127.737z" + id="path1361" /> + + + + + <path + style="stroke:none;" + d="M118.099,144.023h-11.01v-10.78h11.01V144.023z" + id="path1362" /> + + + + + <path + style="stroke:none;" + d="M123.374,119.938h-6.65v19.499h6.65V119.938z" + id="path1363" /> + + + + + <path + style="stroke:none;" + d="M139.892,144.023h-17.205v-15.599h17.205V144.023z" + id="path1364" /> + + + + + <path + style="stroke:none;" + d="M145.396,108.927h-7.34v32.116h7.34V108.927z" + id="path1365" /> + + + + + <path + style="stroke:none;" + d="M80.708,144.023h11.699v-19.817H80.708V144.023z" + id="path1366" /> + + + + + <path + style="stroke:none;" + d="M154.571,143.104h-11.01v-23.396h11.01V143.104z" + id="path1367" /> + + + + + <path + style="stroke:none;" + d="M170.683,145.87h-12.664v-17.662h12.664V145.87z" + id="path1368" /> + + + + + <path + style="stroke:none;" + d="M169.024,142.416h-7.57v-25.003h7.57V142.416z" + id="path1369" /> + + + + + <path + style="stroke:none;" + d="M130.486,138.059h-7.569v-25.005h7.569V138.059z" + id="path1370" /> + + + + + <path + style="stroke:none;" + d="M80.938,144.482h88.086v-7.8H80.938V144.482z" + id="path1371" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M94.063,121.743h2.016v-2.016h-2.016V121.743z" + id="path1372" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M94.063,126.23h2.016v-2.016h-2.016V126.23z" + id="path1373" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M94.063,135.205h2.016v-2.016h-2.016V135.205z" + id="path1374" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M125.874,122.173h2.015v-2.016h-2.015V122.173z" + id="path1375" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M125.874,131.147h2.015v-2.016h-2.015V131.147z" + id="path1376" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M119.207,124.662h2.016v-2.015h-2.016V124.662z" + id="path1377" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M119.207,129.149h2.016v-2.016h-2.016V129.149z" + id="path1378" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M119.207,133.638h2.016v-2.016h-2.016V133.638z" + id="path1379" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M102.489,132.554h2.015v-2.018h-2.015V132.554z" + id="path1380" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M102.489,137.038h2.015v-2.015h-2.015V137.038z" + id="path1381" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M82.507,128.463h2.015v-2.016h-2.015V128.463z" + id="path1382" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M82.507,132.95h2.015v-2.017h-2.015V132.95z" + id="path1383" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M149.356,128.747h2.015v-2.016h-2.015V128.747z" + id="path1384" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M140.674,114.493h2.015v-2.017h-2.015V114.493z" + id="path1385" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M140.674,118.979h2.015v-2.016h-2.015V118.979z" + id="path1386" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M140.674,123.468h2.015v-2.016h-2.015V123.468z" + id="path1387" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M140.674,127.954h2.015v-2.016h-2.015V127.954z" + id="path1388" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M164.085,122.089h2.016v-2.016h-2.016V122.089z" + id="path1389" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M164.085,131.065h2.016v-2.016h-2.016V131.065z" + id="path1390" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M129.602,134.712v2.016h2.016v-2.016H129.602z" + id="path1391" /> + + + + + </g> + + + + <path + style="font-size:12;fill:none;" + d="M 223.0736 130.8574 L 170.8839 238.7107 L 80.48895 176.4417 L 132.6785 68.58834 L 223.0736 130.8574 z " + id="path1392" /> + + + + <g + id="g1393" + style="font-size:12;stroke:#000000;" + transform="matrix(0.291527,-0.602462,0.504941,0.347832,60.33992,183.4832)"> + <path + style="opacity:0.2;stroke:none;" + d="M72.626,58.727c-3.336,0-6.049,2.713-6.049,6.048v132.323c0,3.335,2.713,6.048,6.049,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V64.774c0-3.335-2.714-6.048-6.049-6.048H72.626z" + id="path1394" /> + + + + + <path + style="opacity:0.2;stroke:none;" + d="M71.451,56.378c-3.335,0-6.048,2.713-6.048,6.049v132.322c0,3.335,2.713,6.048,6.048,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V62.427c0-3.336-2.714-6.049-6.049-6.049H71.451z" + id="path1395" /> + + + + + <path + style="opacity:0.2;stroke:none;" + d="M69.103,55.203c-3.335,0-6.049,2.713-6.049,6.048v132.323c0,3.335,2.714,6.048,6.049,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V61.251c0-3.335-2.714-6.048-6.049-6.048H69.103z" + id="path1396" /> + + + + + <linearGradient + id="linearGradient1397" + gradientUnits="userSpaceOnUse" + x1="61.8799" + y1="125.064" + x2="189.4219" + y2="125.064"> + <stop + offset="0" + style="stop-color:#616161" + id="stop1398" /> + + + + + <stop + offset="1" + style="stop-color:#464646" + id="stop1399" /> + + + + + </linearGradient> + + + + + <path + style="fill:url(#XMLID_3_);stroke:none;" + d="M67.928,52.854c-3.335,0-6.048,2.713-6.048,6.049v132.322c0,3.335,2.713,6.048,6.048,6.048h115.444 c3.336,0,6.05-2.713,6.05-6.048V58.903c0-3.336-2.714-6.049-6.05-6.049H67.928z" + id="path1400" /> + + + + + <path + style="fill:#FFFFFF;stroke:none;" + d="M184.725,191.226c0,0.747-0.606,1.351-1.353,1.351H67.928c-0.745,0-1.351-0.604-1.351-1.351V58.903 c0-0.746,0.605-1.352,1.351-1.352h115.444c0.746,0,1.353,0.605,1.353,1.352V191.226z" + id="path1401" /> + + + + + <linearGradient + id="linearGradient1402" + gradientUnits="userSpaceOnUse" + x1="233.4971" + y1="232.6162" + x2="51.0675" + y2="50.1867"> + <stop + offset="0" + style="stop-color:#CECEDB" + id="stop1403" /> + + + + + <stop + offset="0.691" + style="stop-color:#E9E9EF" + id="stop1404" /> + + + + + <stop + offset="1" + style="stop-color:#FFFFFF" + id="stop1405" /> + + + + + </linearGradient> + + + + + <path + style="fill:url(#XMLID_4_);stroke:none;" + d="M69.954,60.635c0,4.134,0,123.846,0,127.978c4.104,0,106.998,0,111.101,0c0-4.132,0-123.844,0-127.978 C176.952,60.635,74.058,60.635,69.954,60.635z" + id="path1406" /> + + + + + <linearGradient + id="linearGradient1407" + gradientUnits="userSpaceOnUse" + x1="-654.9922" + y1="23.1128" + x2="-520.0903" + y2="23.1128" + gradientTransform="matrix(0 1 -1 0 148.7637 686.498)"> + <stop + offset="0" + style="stop-color:#BDBDCF" + id="stop1408" /> + + + + + <stop + offset="0.1923" + style="stop-color:#CCCCDA" + id="stop1409" /> + + + + + <stop + offset="0.7201" + style="stop-color:#F1F1F5" + id="stop1410" /> + + + + + <stop + offset="1" + style="stop-color:#FFFFFF" + id="stop1411" /> + + + + + </linearGradient> + + + + + <path + style="fill:url(#XMLID_5_);stroke:none;" + d="M176.563,67.165c-3.289,0-98.536,0-101.823,0c0,3.266,0,85.131,0,88.396c3.287,0,98.534,0,101.823,0 C176.563,152.296,176.563,70.431,176.563,67.165z" + id="path1412" /> + + + + + <path + style="stroke:none;" + d="M78.46,70.886c0,6.529,0,74.425,0,80.955c6.646,0,87.734,0,94.381,0c0-6.53,0-74.426,0-80.955 C166.194,70.886,85.106,70.886,78.46,70.886z" + id="path1413" /> + + + + + <radialGradient + id="radialGradient1414" + cx="109.7783" + cy="116.7822" + r="66.1491" + fx="109.7783" + fy="116.7822" + gradientUnits="userSpaceOnUse"> + <stop + offset="0" + style="stop-color:#B1E1FF" + id="stop1415" /> + + + + + <stop + offset="0.1441" + style="stop-color:#AEDFFF" + id="stop1416" /> + + + + + <stop + offset="0.2661" + style="stop-color:#A3D9FF" + id="stop1417" /> + + + + + <stop + offset="0.38" + style="stop-color:#92CFFF" + id="stop1418" /> + + + + + <stop + offset="0.489" + style="stop-color:#79C1FF" + id="stop1419" /> + + + + + <stop + offset="0.5937" + style="stop-color:#5AAEFF" + id="stop1420" /> + + + + + <stop + offset="0.6011" + style="stop-color:#57ADFF" + id="stop1421" /> + + + + + <stop + offset="1" + style="stop-color:#0035ED" + id="stop1422" /> + + + + + </radialGradient> + + + + + <path + style="fill:url(#XMLID_6_);stroke:none;" + d="M80.94,73.367c0,4.515,0,71.477,0,75.993c4.577,0,84.844,0,89.42,0c0-4.517,0-71.479,0-75.993 C165.784,73.367,85.518,73.367,80.94,73.367z" + id="path1423" /> + + + + + <linearGradient + id="linearGradient1424" + gradientUnits="userSpaceOnUse" + x1="100.4385" + y1="122.5811" + x2="118.1257" + y2="104.8938"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop1425" /> + + + + + <stop + offset="1" + style="stop-color:#E9E9EF" + id="stop1426" /> + + + + + </linearGradient> + + + + + <path + style="fill:url(#XMLID_7_);stroke:none;" + d="M96.774,113.736c0,6.907,5.6,12.508,12.507,12.508s12.506-5.601,12.506-12.508 s-5.599-12.507-12.506-12.507S96.774,106.829,96.774,113.736z" + id="path1427" /> + + + + + <path + style="stroke:none;" + d="M78.46,137.866c0,1.127,0,12.845,0,13.975c6.646,0,87.734,0,94.381,0c0-1.13,0-12.848,0-13.975 C166.194,137.866,85.106,137.866,78.46,137.866z" + id="path1428" /> + + + + + <path + style="stroke:none;" + d="M100.665,117.415h-8.486v25.92h8.486V117.415z" + id="path1429" /> + + + + + <path + style="stroke:none;" + d="M108.007,127.737h-8.488v15.828h8.488V127.737z" + id="path1430" /> + + + + + <path + style="stroke:none;" + d="M118.099,144.023h-11.01v-10.78h11.01V144.023z" + id="path1431" /> + + + + + <path + style="stroke:none;" + d="M123.374,119.938h-6.65v19.499h6.65V119.938z" + id="path1432" /> + + + + + <path + style="stroke:none;" + d="M139.892,144.023h-17.205v-15.599h17.205V144.023z" + id="path1433" /> + + + + + <path + style="stroke:none;" + d="M145.396,108.927h-7.34v32.116h7.34V108.927z" + id="path1434" /> + + + + + <path + style="stroke:none;" + d="M80.708,144.023h11.699v-19.817H80.708V144.023z" + id="path1435" /> + + + + + <path + style="stroke:none;" + d="M154.571,143.104h-11.01v-23.396h11.01V143.104z" + id="path1436" /> + + + + + <path + style="stroke:none;" + d="M170.683,145.87h-12.664v-17.662h12.664V145.87z" + id="path1437" /> + + + + + <path + style="stroke:none;" + d="M169.024,142.416h-7.57v-25.003h7.57V142.416z" + id="path1438" /> + + + + + <path + style="stroke:none;" + d="M130.486,138.059h-7.569v-25.005h7.569V138.059z" + id="path1439" /> + + + + + <path + style="stroke:none;" + d="M80.938,144.482h88.086v-7.8H80.938V144.482z" + id="path1440" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M94.063,121.743h2.016v-2.016h-2.016V121.743z" + id="path1441" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M94.063,126.23h2.016v-2.016h-2.016V126.23z" + id="path1442" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M94.063,135.205h2.016v-2.016h-2.016V135.205z" + id="path1443" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M125.874,122.173h2.015v-2.016h-2.015V122.173z" + id="path1444" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M125.874,131.147h2.015v-2.016h-2.015V131.147z" + id="path1445" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M119.207,124.662h2.016v-2.015h-2.016V124.662z" + id="path1446" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M119.207,129.149h2.016v-2.016h-2.016V129.149z" + id="path1447" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M119.207,133.638h2.016v-2.016h-2.016V133.638z" + id="path1448" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M102.489,132.554h2.015v-2.018h-2.015V132.554z" + id="path1449" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M102.489,137.038h2.015v-2.015h-2.015V137.038z" + id="path1450" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M82.507,128.463h2.015v-2.016h-2.015V128.463z" + id="path1451" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M82.507,132.95h2.015v-2.017h-2.015V132.95z" + id="path1452" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M149.356,128.747h2.015v-2.016h-2.015V128.747z" + id="path1453" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M140.674,114.493h2.015v-2.017h-2.015V114.493z" + id="path1454" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M140.674,118.979h2.015v-2.016h-2.015V118.979z" + id="path1455" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M140.674,123.468h2.015v-2.016h-2.015V123.468z" + id="path1456" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M140.674,127.954h2.015v-2.016h-2.015V127.954z" + id="path1457" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M164.085,122.089h2.016v-2.016h-2.016V122.089z" + id="path1458" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M164.085,131.065h2.016v-2.016h-2.016V131.065z" + id="path1459" /> + + + + + <path + style="fill:#FFF030;stroke:none;" + d="M129.602,134.712v2.016h2.016v-2.016H129.602z" + id="path1460" /> + + + + + </g> + + + +<g + id="g2009" + transform="matrix(2.357251,0,0,2.832578,34.95087,61.80306)"> + <defs + id="defs232" /> + + + <sodipodi:namedview + id="namedview2013" /> + + + <g + id="Layer_1" + stroke="#000000"> + <path + id="path170" + fill="none" + stroke="none" + d="M3.621,36.109l11.775-25.814c0.321-0.971,3.194-2.688,4.462-2.461l22.103,1.858 c-0.26-0.156-0.566-0.274-0.91-0.335L19.46,7.218c-1.269-0.226-4.141,1.491-4.462,2.461L3.224,35.494 c-0.183,0.553-0.011,1.106,0.406,1.521C3.528,36.729,3.519,36.42,3.621,36.109z" /> + + + <g + id="Layer_2"> + <path + id="path241" + fill="none" + stroke="none" + d="M48,48H0V0h48V48z" /> + + + </g> + + + <g + id="g171" /> + + + <linearGradient + id="path205_1_" + gradientUnits="userSpaceOnUse" + x1="-235.5874" + y1="318.7158" + x2="-234.9869" + y2="318.1155" + gradientTransform="matrix(0.3069 0 0 -0.3101 81.6819 119.6117)"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop160" /> + + + <stop + offset="0.3726" + style="stop-color:#FDFDFD" + id="stop161" /> + + + <stop + offset="0.5069" + style="stop-color:#F6F6F6" + id="stop162" /> + + + <stop + offset="0.6026" + style="stop-color:#EBEBEB" + id="stop163" /> + + + <stop + offset="0.68" + style="stop-color:#DADADA" + id="stop164" /> + + + <stop + offset="0.7463" + style="stop-color:#C4C4C4" + id="stop165" /> + + + <stop + offset="0.805" + style="stop-color:#A8A8A8" + id="stop166" /> + + + <stop + offset="0.8581" + style="stop-color:#888888" + id="stop167" /> + + + <stop + offset="0.9069" + style="stop-color:#626262" + id="stop168" /> + + + <stop + offset="0.9523" + style="stop-color:#373737" + id="stop169" /> + + + <stop + offset="0.9926" + style="stop-color:#090909" + id="stop170" /> + + + <stop + offset="1" + style="stop-color:#000000" + id="stop171" /> + + + + + + + + + + + + + + + + + + + + + + + + + + </linearGradient> + + + <path + id="path205" + fill="url(#path205_1_)" + stroke="none" + d="M10.011,20.379c-0.109-0.062-0.233-0.1-0.367-0.1 c-0.421,0-0.763,0.346-0.763,0.771c0,0.115,0.027,0.223,0.072,0.321C9.106,20.878,9.509,20.498,10.011,20.379z" /> + + + <path + id="path206" + opacity="0.36" + fill="#999999" + stroke="#999999" + stroke-width="1.5343" + stroke-miterlimit="1.2275" + d=" M6.653,25.182c0,11.198,8.983,20.275,20.064,20.275c11.082,0,20.064-9.076,20.064-20.275c0-11.198-8.982-20.275-20.064-20.275 C15.636,4.906,6.653,13.984,6.653,25.182z" /> + + + <path + fill="#990000" + stroke="none" + d="M2.046,22.701c0,11.731,9.449,21.275,21.064,21.275c11.615,0,21.065-9.544,21.065-21.275 S34.726,1.425,23.11,1.425C11.496,1.426,2.046,10.97,2.046,22.701z" + id="path197" /> + + + <linearGradient + id="path213_1_" + gradientUnits="userSpaceOnUse" + x1="-232.3296" + y1="353.9814" + x2="-137.7065" + y2="259.358" + gradientTransform="matrix(0.3069 0 0 -0.3101 81.6819 119.6117)"> + <stop + offset="0" + style="stop-color:#FB7462" + id="stop199" /> + + + <stop + offset="1" + style="stop-color:#FC2829" + id="stop200" /> + + + + + + </linearGradient> + + + <path + id="path213" + fill="url(#path213_1_)" + stroke="none" + d="M3.046,22.701c0,11.198,8.983,20.275,20.064,20.275 c11.082,0,20.065-9.077,20.065-20.275S34.192,2.425,23.11,2.425C12.03,2.426,3.046,11.503,3.046,22.701z" /> + + + <radialGradient + id="path239_1_" + cx="-141.856" + cy="240.4365" + r="94.1356" + fx="-141.856" + fy="240.4365" + gradientTransform="matrix(0.3069 0 0 -0.3101 81.6819 119.6117)" + gradientUnits="userSpaceOnUse"> + <stop + offset="0" + style="stop-color:#000000" + id="stop206" /> + + + <stop + offset="1" + style="stop-color:#FF3333" + id="stop207" /> + + + + + + </radialGradient> + + + <path + id="path239" + opacity="0.4" + fill="url(#path239_1_)" + stroke="none" + d="M41.881,18.448c-2.748-1.396-5.852-2.184-9.139-2.184 c-3.571,0-6.928,0.929-9.846,2.561h15.301v0.446h0.387v8.345H14.529c-1.383,2.778-2.163,5.917-2.163,9.239 c0,1.053,0.079,2.086,0.229,3.097c2.839,1.556,6.093,2.439,9.549,2.439c11.082,0,20.065-9.078,20.065-20.276 C42.21,20.864,42.097,19.638,41.881,18.448z" /> + + + <g + id="g214"> + <path + id="path215" + fill="#B11C1D" + stroke="none" + d="M39.549,28.201H6.989v-8.344h32.56V28.201z" /> + + + <g + id="g216"> + <path + id="path217" + fill="#FB9A86" + stroke="none" + d="M39.162,27.812H6.672v-8.4h32.49V27.812z" /> + + + <path + id="path218" + fill="#FFFFFF" + stroke="none" + d="M39.162,27.812H7.059v-7.913h32.103V27.812z" /> + + + <linearGradient + id="path225_1_" + gradientUnits="userSpaceOnUse" + x1="-190.8691" + y1="300.7178" + x2="-190.8691" + y2="330.2731" + gradientTransform="matrix(0.3069 0 0 -0.3101 81.6819 119.6117)"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop218" /> + + + <stop + offset="1" + style="stop-color:#CCCCCC" + id="stop219" /> + + + + + + </linearGradient> + + + <path + id="path225" + fill="url(#path225_1_)" + stroke="none" + d="M7.385,22.834c0-1.293,1.038-2.342,2.318-2.342H38.28 c0.313,0,0.61,0.063,0.882,0.177v-0.771H7.059v7.913h0.326V22.834z" /> + + + </g> + + + </g> + + + <linearGradient + id="path232_1_" + gradientUnits="userSpaceOnUse" + x1="14.1377" + y1="12.6128" + x2="4.2627" + y2="2.7378"> + <stop + offset="0" + style="stop-color:#FB7462" + id="stop225" /> + + + <stop + offset="1" + style="stop-color:#FFFFFF" + id="stop226" /> + + + + + + </linearGradient> + + + <path + id="path232" + fill="url(#path232_1_)" + stroke="none" + d="M8.45,18.411c4.288-7.016,11.965-11.692,20.725-11.692 c2.281,0,4.488,0.318,6.582,0.911c-3.458-3.244-8.089-5.229-13.18-5.229C11.875,2.401,3.2,11.168,3.2,21.983 c0,2.837,0.598,5.53,1.671,7.965c0.094-1.738,0.369-3.429,0.802-5.052v-6.485H8.45z" /> + + + </g> + + +</g> + +</svg> 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|||||[|[[[[[[[[<<k///////////^54///^,~,~,~!~!~~~~''''',@@@@@@@> ", +" 000000c;;!;;;;;;;;;;;;;f;f;f;fff;ffffffffffffgfggfggggggggghghgghghhhhhghhhhhddhdhhdhdhddhdhdhddhdcdhddddddhddhdhdhdddddhddddddhdddddddddddddddddddpdpdpppdpdpdpdopdppdpdppppppppppdppppppppppppopvppppa00000000)####..+.++).#*,,'''''',,,,!,!'~,~'~'~'~'~,,~~******~***&^//////_}[[[<[<[<[[[<[<[<[<[<<<<[<<<<<<<<<<<:<<:<::::::4::4:444444454555555556565////#~~~,~~~!~!!!~~!'''',,&@@@@@@@$//1811333|7|||||[|}[[[<<[<<k///////////^55////,~,,,~!~~~!~~,'''',@@@@@@@> ", +" 000000c*;;;;;;;;;;;;;;;f;;f;f;f;ff;ffffffefgffgfgfggfgggggggggghghghghhhghghhdhhhddhdhhdhdhddchddhddhddhdhdhddddddddddhdddddddddddddddddjddddddpddddpdddddpddodpdpdpdpdppdpdpppppppppppppppppppvpppvpppppa00000000+#####$+.$#-,'''''''',',,,,',,','!'~'!,~'~~**~***~**~&>//////^k[[[<[[[[[[[<[[[[<[[[<[[[<[<[<<<<<<<<<<<<:<:<:::::::::4:4444445445555555655n////*~~!~~!~!~~~~~~,'''',,+@@@@@@@@/111113337||||[|[|[[[[<[<<k///////////^45///(,,~,~,~~!~~~~'''',,$>$@@@@> ", +" 000000b;;;;;;!;;;;;;;;;;;;f;f;f;ffffffffefgffgfgfggggggggghghghhghhhhhhhhhcdhhdhdhhhdhdhdhdhdhdhdhdhdhddhddddhdhddhdhddddhddddcddddhdddddddddddodddpdddodpdppdpdpddpppdodpdopppppppdoppoppopppppppvpppppvppj00000000######+.&,''''''''',!,!'!!!!'~!,~'~'!'~{~*{***~***q$//////^2[[[[[[[[[[[[[[[[[[[<[[[<[[<[<[<}<<}<<<<<<<<:<<:<:::::4::4:4444444445555555656^///+~~~~,~!~!~!~!~!''''',*@@@$>@@@^^$$$^1_11122122[[[[[[<[<<k///////////^54////~'~,,,~~!~~~~,'''',@@@@@$@> ", +" 000000c;;;!;;;;;;;;;;;f;f;;f;f;f;f;fffffffffgfgfggefggggggggghgghghghhghgdhdhdhhdhdhdhdhdhhdhdhddhdhdddddhddhddhddddddddhddddddddddddddddddddddddddddodddpdpddddodpppdppdpppdpddppppppdppdppppppoppppvopvvvpoa0000000b#####%!'''''''',,!',,,!''~'~',',,,!,~~*********&.///////_}[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[<[<[<<}<<<<<<<<:<<:::::::4::4:4444544455555556n////&,~~~~!~!~!~~!~,'''',,#@@@@$@@@//////////////$[|[[[[<[<<k////www(///^55///^'~,!!!~~~~~~~''''',@@@@@@@> ", +" 000000bl;;;;;;;;;;;;;;;;;f;f;f;ffffffffffffgfgfgfgggggggggghghghghhghhhhhhdgdhdhdhdhhdhhdhddhdhdhdhdhdhdhdhddhddddhdhdcddddddddddddddddddddddjddddpdddddpdpdpdoddppddpddppdpdppodpppppvppvpppppppppopppppvppvpoa0000000b$#%=*],'''''',,,,,!',,,,,,'~,~'~'~~****~***m&$^//////^k}}}[[[[[[[[[[[[[[[[[[[[[[[[[[[[<[[[[<[[<<<}<<<<<<<<:<:<::::::4:4:4444454455555556_///>~~~,~!~!~~!~~~~''''',~@@@@@@@@>/////////////^[[[[[<[<<<k////www(///^55////!!,~'!~~~~~~~,'''''@@@@@@@> ", +" 000000l;;;;;;;;;;;;;;;f;;f;f;f;ff;fffffefgfffgfgfgfggggggggggghghghhghhhdhdhdhdhhdhdhcdhdhdhdddhddhddddhddddhdddcddddhdddhdddhdddddddddpddddddpdddodddpddpdpdpdppddppoppdodpppppppppppppppppppopvpppvvvpvvpvvvpp00000000)-*]]]]'''',!'',,'~'!,!'~,,,'~'~~****&#$+$++>//////^2222k}}}[|[|[|[|[|[|[|[|[[[[[[[[[[[[[[[<[[[[<[<<}<<<<<<<<:<:::::::4::4444454445555555////%~~~!~!~!~!~!~~!'''',,&@@@@@@@@////////////(_|[[[[[<[<<k///(www(///^45////,,,,~,~~!~~~~'''',,@@@@@@@> ", +" 000000c;;!;;;;;;;;;;;;;;;f;f;f;ffffffffffffgfgfggggggggggghghghgdghhhhhchdgdhhhdhdhdhdhdhdhdhdhddhddhdddddhdddhdhdddddddddddddddddddddjdddddddddpdddddpdpdpddpdddpppddddpdpppppppppppppppdodppppppvpvpvppvppvvvovo00000000=!]!]]],',,,!,!,'!'~'!''!'~'~~**{*&@@@@@@@$@>(///_2k222223}}}|[[|[|[|[|[|[|[|[|[}[[}[[[[[[[[[<[[<[<<<}<<<<<:<:<:::::::4:4:44444545555555n////~!~~!~~~~~!~!~~,'''',,+@@@@@@@>////////////$[[[[[<[<<<k////sss////^55////,~,~'~~~~~~!~''''',@@$>$@@> ", +" 000000c;;;;;;!;;;;;;;;;f;f;f;ff;f;ffffffffgefgfgfgfgggggghggghghghghghdhhhhhdchdhhhdhhdhdhcddhdhddgddhddhhddhdhdddhdddhddddddddhdddddddddddpdpddpdpdddddpddpppdpppdodpppppdpppdpppppppppppppppppopppvppppvvpvvppvppj0000000c=]]]]*]~',,,','~,,,'~'~'~,~~****+@@@@@@@@@+@+>^22222222222}3}||||[|[|[|[|[||[|[|[|[|[[[[[[[[[[[[[[[<<<}<:}<<:<<<::::::::4:44444544555555////.~,~~!!,~!~~!~!!'''',,*@$@@@@@@(///////////_|[[[[<[<<<k///////////^45///(~',,,,~~~~~~~,''',,@@@@@@+> ", +" 000000bl;;;;;;;;;;;;;;f;;f;f;ff;feffffefffgfgfggfgggggggggghghghgdghhdhdhddhhhdhdhdhhdddhhddhdhdcdddcdhdddddddddddddhdddhddddddddddddddddddddddpddddppdoddpdddpdpdpdpdddodppdopppppppppppopppppppppoppopvvpvppvvvvvvvj0000000)]]]]=]=~'!'~,,''~'~'~',~{*****.$@@@$@@@@@@@@$@$_2_222222222}3}||||||||||[||[|[|[|[|[|[|[}[[[[[[[<[[[<<[<<<<<<:<:<::::4::4:4444445455555n////&,~,~~~~!~!~~~~,'''',,#@@@$@@@>///////////^|[[[[[<[<<k///////////^55////,~~,~'~~~~~~~''''',@@@@@@@> ", +" 000000e!;;!;;;;;;;;;;;f;f;f;f;ff;ffffffgefffgfgeggggggggghgghgghghhhhdhhdgdhddgdhdhdhhdhdhddgdddhhdddhddhdhdcdhdddddddddddddhddddddddddddddpdpdddpdddddpddppdodpdpdpdoppddpppdppppppppppdpppppoppvpvvpppvpvvovvpppvvooa0000000l==]]*]]~,,'~'~,,,,,,!*~**{*~#@@@@@@@@@@@@@@@@@@$222222222222}3}|||||[||||||||||[|[|[|[|[[}[[[[[[[<[[<[<<}<<<<<<<::::::4::44444445455565_///>!~!~,~~~~~!~!~~''''',*@@>@@$@@>/////////(_[[[[[<[<<<k///////////^45////''~'~,~~!~~~~'''',,@@@@@@@> ", +" 000000c;;;;;;;;;;;;;;f;;;f;f;ffffffffffffgfgfgfggfggggggggggdghgdghcdhhdgdhdhhddhhdhddhddhdhdddhdddhdhdddhdddddddhddhddhddddddddddddddpddddddddjddddjdddpdpdpdddodpdppddpppdppppppppppodoppppopppvpppvpvpvppvppvvvvvvvvo0000000b-==]*=*]~'~',''~'~'~*******&$@@@@@@@@@@@@@@@@@@@>$221222221223}3}|||||||||||||||||[|[|[|[|[[[}[[[[[[[<[<<<<}:<:<<:<:::::4::4444445454554////&~!~!~,!~!~!~!~,''''',&@@@@@@@@//////////^|[[[<[<<<<k///////////_55////~'~',,~~~~!!~''''',@@@@$@@> ", +" 000000c;;;;;;;;;;;;;;;f;f;f;f;ff;ffefffffffgfgfgggggggggghghggghhgdhhdhdhdhdhdhdhdhdhhhdhhhddhddgdhddhdddhddhdhddddddddddddhdddddddddddddddddpddddpdddpddpppdpdppdpppddodppopppppppppppdppppppppvvvvvppvvvpvpvvvvvvvvpvvvj0000000b=*=]]]*]!,,,~,',~*{**~**~*#@@@@@@@@$>$@@@@@@@@@>@^$111212321223333|||||||||||||||||||[||[|[|[[[[[[[[[[[[[<<<<<<<:<::::::4::444444455555_////~!~,~~!~!~~~~~~,'''',,+@@@@@@@>////////(_[[[[[<[<<<k///////////^44///^,,,,~,~~~~~~~,''',,@@$>@@@> ", +" 000000bl;;;;;;;;;;;;;f;;f;f;fff;fffffffegffgfgfgfgggggggggghghhgdhdhhhhhhhhhhdhhhdhhdcddhdddhdhddddhddhdddhddhddhdhdhdhdddhddddhdddddddddddjddddddddpddpdpddpdodppdpdpppdpddpdpppppppppopppppoppppppppppppvpopvvvvpvvvvpvvvj0000000)===]=*]*!'!'~~*********~&@@@$@@$>)*&+@@@@@@@@@$@@@$121121213223}33||7|||||||||||||[|||[|[|[|[|[[[[[[<[<[<<}<<<<<<<:<::::4:4:44444455555////.~!~,!~~~!~!~~~!''''',-@@@@@@@@/////////$[[[[[<[<<<k///////////_55////'~'~',~~~~~~~''''',@@@@@@$> ", +" 000000l;;!;;;;;;;;;;;;;f;f;f;ff;ffffffffffgfgfgggggggggghgghghghghdhdhdhdhddhhdhdhdhhhdhdhddhddhdhddhdddhcddhdddddddddddddddddddddddddddddddddpdpdpddpddpdppdddpdppdodpdppppppppppppppdpppppppvvoppppvvovvpvpvppopvvvpvvvvooa0000000-==*]]=]~~'~~~*****~***&@@+>@@@@#,,,,&$>$@@@@@@@@>>>$1111211211233337|7|||||||||||||||||||[|[|}[[[[[[[[[[[<}<<<<<:<:<:::::4:44444445455n////*,~~,!,~!~!~!!~,'''',,#>$@@@@@@(uu(////^[[[[<[<<<<k///////////^45////!'!,!,~~!~~!~,''',,@@@@@@@> ", +" 000000c;;;;;;;;;;;;;f;f;f;f;fff;fffffffffgfgfeggfggggggghgghghgdddhhhhhdhhdgdhdhdhdhddhdhhdhddhcddhddhdhdddddddhdhddhdhddddddddddddddjhpddddpdpdddddpdpdpdpddpppdpdppdpdodpppppppppppopppppppppppvpoppppppvpvvpopvvvvvvvvpvvvo0000000b===*]=]=*~**{***~***&^^@@@$@@@+~'',,,,%>@@@@@@@@@@@>>11112112121233337|77|7||7|||||||||[||[|[|[|[}[[[[[<[<<[<<<<<<<:<:::::4:44444454555^///>~,~!~~~~!~~~~~!''''''~@@@@@$@@utxs////^[[[[[<<<<<n///////////^55///(~,~'~,~~!]~!~''''',@@@@@@@> ", +" 000000;;;;;;;;;;;;;f;;f;ff;f;ffffefffgfgfgfggfgggggggggghgghghhhhhdhdhhhhdcdhdhdhhdhdhdhddhdhdhdhddhddddhdhddhdddddddddddddddddddddddddphoddddddjddppdddodppppdppppdppdppppppppppdodpdpppppppvppppvpopvvvpvvvpvvvvvvvvvvvvvvvoo0000000%-===]=*-****~*****$//+@@@@@@@%,''',,,,*#$@@@@@@@@$@>@^$111131132123337|777|7|77||||||||||||||[||[[[[[[[[[<[<}<<<<<:<::::::4:44444454554////&~,~,,~!~~~~~!~,'''',,&@@@@@@@@ttu///(^}[[[<}}k<kk///////////^44////,'',,,~~~~~~~,''',,@$@@@@+> ", +" 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|||||||[|[[[[[[[<[<<<<k^///////////45n////=!,~,,,~~~!~]!~,'''',,#@@@@@@@@/////////////////////////,,',,,~~~~~~~,''',,@@@$@@+> ", +" 000000e;f;f;;f;ff;fffffffffffggfgfggegfgggggdghggdhhdhdhdhhdddhddhhhdhhddcdhhdddhdddgddhdddddhddddddhdddhdhcddhddddddddddpdddddpddpdddddjdpdpdpdppdpppddpdjddpdodpppppdodopppppppopvpppvppppopvpvpvpvypypvpvvypvvypyvpyvvyvvvvvvvvyvvyvvvyvvyvyyyvyyoa000000)$#####-*]]~]*~,!~,~~!~,~~******~*{***~~~!~!~!~!!~!~~~,''''''''''!%@@@@@@@@@@@@@@$11$118181133333777|||||[|[|[[[[<[<[<<<k///////tu////_54_///>,,,!,'!~~~~~~~~'''',,*@@@@$@@@(////////////////////////',,,'!~~~~~~~''''',@@@@@@@> ", +" 000000c;;;;f;f;f;f;ffeffffffffegfgfggggggggggghghgdgdghdgdhdghhdgdhdhdhhhdddhdchddgdddddhdhdhdddhdhddddcdddddddddddddddddddddddddddddpdddddpdphoddppddodpddddopdppppppppppppppppppdppopppppppvvpvvvvpppvvpyvpppypvvpvvpvvvvvyvyvyvvyvyvyvyvyvyvvyyvyyyj000000b.##%#-==]]]]=~~,~!,~,~~~*****{*****~***~~!~!~!~!!~,~!~~,''''''''',~#@@$@@@@@@@@@@@$1111111$1123333|7|||||[||[[[[[[[<[<<<_////uxww/////n5n////&~'!,!,~~!~~~~~!'''',,&@@@@@@@@////////////////////////,,,,,,~~~~~~~'''',,@@@@@@@> ", +" 000000ef;f;;f;ff;fffffffeffgfgfgfggfgggggggggghgddhdhddhdhhddhdhddhhdhddhhhdhdhddhddhdhddddhddhddddddhdddhddddddhdddddddjdddddjddpddjddpdpddppdpdpdpppdpdodppdppppppppppppppppppopvppvpvpopvvpppopypvopypvpvypvvpypyvyvyvvypvvvvvyvyvvvyvyvvyvyvyvvyyoya000000+####====]*==*~!!~~,~,~**~*~*&@&~~**~~**~*~~!!~!~~!!~!!~!,'''''''','*+@@@$>@@@@@@@@$$111$//11823333|7|||||[|[|[[[[[<[<<<k////uwwwt////^55_///(~,!,,'!~~~~~~~~,'''',,@@@@@@@@>///////////////////////,'~',,~~*~~~~''''',@@@@@$@> ", +" 000000a;;;f;f;f;fff;ffffffffgfffgggfgggggggdghgghhhhhhdghdhhhhhdhhdhhdhhddhdhdddgddddhdhdhdddhddhddhdddddddddddddddddddddhddddddddpdddddpdpdpdpppdppdpdppdpdppppdppdodpppppppopppdvvppppppvpvpvvppppyvpvppyppypyvovpypvvyvvvyvvyvvyvvyvyvvyvyvyvyyvyvyyoa000000b.##--===]=**=*~,!,~~~~******^/^.q~****~*~~~~!~!!!~!~!~!~!,,''''''''',-$@@@@@@@@@@@@@$^////^111113333||||||[|[[[[[[[[<<<<_////xwx(/////n55////#~',!!,~!~~~~~~!'''',,&@@@@@@@@///////////////////////'','',~~~~~~~,''',,@$@@@@@> ", +" 000000cf;f;f;f;ff;ffffffffgfgfggfgggggggggggghgdhdhdhdhddgdhddhdhdhdhhdhdhdhddgdddhhdhddddhdhdddddhdddhddddddddddddddddpddddddddpdddpddpddppdpdddodpdpppdpdppdodppppdppodpppdpppopvppvpopvpvpvvvvvvppvypypvpyppypvvvpvvvvvvvyvvyvyvvvyvyvyvvvyvyvyyvyvyyy000000b.##========*=*~!~~,~~*~****$/////&**~*~***~~~~~!~!~!~!!~~!!,''''',''''!%@@$>$@@@@@@@@@>////11312123333|||||||[[[[<[[[<<<<^///(s////////45n////*!,,,,,~~~~~~~~,''''',#@@$@@$@>//////////////////////,,'~'!~~~~~~~,'''',@>@@@@@> ", +" 000000e;;;f;f;ffffffffffffgfgfgefgfggggggghggghgdgdhhhhhdhdgdhchhdhdhdhdhddgddddhddhddhdhdddddhddddddddhddddhddddddddddddddddddpdpddddddpdddodppddpdpdppdodpppppppppppppppppvppppoppppppvpvpvpypppyvpvppvpyppypvvvvyvvyvyvvvvvvvvyvvyvvvvyvyvyvyvyvyyyyooa000000).*==-==-=*=*=*,~,~~*****~&///////^&***~~~***~~~~,~~~!~!!~~~~,''''''''',~#@@@@@@@@@@@@@@(//^11113112333|||[|[|[[[[<[<[<<<k/////////////_55^///@'!'!,,!!~~~~~~~'''',,*@>@@@@+@(/////////////////////,,',',~~*~~~~''''',@$@@@@@> ", +" 000000a;f;f;fff;ffffffeffgfgfgfggggggggggggghhgddhdhhdhdhhchdhdhddgdhdhdhdhdddhdddhdddhdddhddhdddhddhdddddddddddddddddddddddjddpddddpdpdpdpdpdpddppdpppdppdppdpppppppppppppopppoppppopvpyppvpvpppyppvvvvypvpvvpyvyvpvyvvvvvvvvyyvyvyvyvyvyvvyvvyvyvyvvvyyyj000000)%]]===-=-*=*=*~!~,~*~r~**@/////////>m~**~~~~~~~~~,~,~~!!!!!~~!,''''''''''*.@@$>@@@@@@@@@>//^18112312233}||||[|[[[[[[<[<<<^/////////////n54////&!,'!'!~~~~~~~~,'''',,&@@@@@@@@/////////////////////',,,'!~~~~~~~''''',@@@@@$@> ", +" 00000ac;f;f;f;ff;ffffffffgfgfgfgfggggggghghggdgdgdgdhhdgddgddgdhddhdhhdcdhdhddgdhcdhddhdhddcdhdddhdddhddddhdddddhddddddddddddpdddpddpddppdpdpdjppdppddppdpppppppppdopppppppdpppppvpppvpppvpvpopyppvpypvpvpypyyppvvvvvvvyvvyvyyvvyvyvyvyvyvyyyyvyvyyyvyyvyyo0000000*]!]=-*-==*=*=*,~~******&////n///////$*~*~**~*~~~~~~!~!~~~~!~~~!,''''''''',-+@@$@@@@@@@@@+>/111111821223}3}|[|[[[[[[[<<<<k/////////////^55_///(!,!,,,,~~~~~~~~,''',,!$@@@@@@@>////////////////////,','',~~~~~~~,'''',@@@@@@@> ", +" 000b00l;f;f;fffffffffffgfefggggggggggggggdghgdhdhddhcdgddhddhddhhhdhddhdhddhdddddddhddddddhdddddddddhddddddddddddddddjdddddddddpdddpddpdpdpdpdddppdododppopppppppppppppopppppoppvpppvpvpyppoppvpypyppypypvvpvpypyvvovvyvvvvvvyvvvvvvvvvvvvvvyyvyvyvyvvyyyyya000000-]=!*=-=&=*=*=*~~~***~**^///_66n///////#*~~*~*~*~~~~~~!!!!!!!~!~~,'''''''''',-@@@@$@@@$@@@@@>111121131213}3}||[|[[[<[[<<<:_/////////////nn/////#,,,,!'~~~~~~~~!''''',-@@@@@@@@////////////////////,',,',~~~*~~~''''',@@@@@@@> ", +" 000000a;f;ff;f;ffffffgfffggfgffgfgggggghggghghhhhdgdhhddhhdgdhdhdhhdhhdhddgdddgdhdhdhdhdhddddhddhdhddddddddddhdddddddddddddddpddddjddpdpdppdpdppdpddpdpddpdppppppppppppdpppopppvppvvpvppppvvpypppypvpvppvpvypypyvpvvyvvvvvvyvyvyvyyyvyyvyyyyyvvyvyyvyvyyvoyyj000000c]]]*=-=--=*=***~*~****#////66666_//////^&*~*~~~~~~~~~!~!~!~!~!~!~~,''''''''',!%$>@@@@@@@$@@@>$11121131222322/2[[[[[[<[<<<</////////////////////*'!',,,~~~~~~~~,''',,,$@$@@@@@>///////////////////',,,',~~~~~~~,''',,$>$@@@@> ", +" 000000eff;f;ffffffffeffgfffgggggggggggggghghgdhdhhdhdhdgddgddgdhdhdhddgddhddgddddhdddddhddhddddhddddhddddddhdddddddddddddjddpdddddddppphjdpddppdpdppdpdopppppppppppppopppppppppvppppopvvpyppvvvypvyvypyypypvpypvvypypvyvvypvvvvyvvvyvvyvvyvyyyvyvvyyvyvyyyyoy0000000*]]=*-*-=-*==******~*&^///n6666666_//////^-~*~*~~~~~~~~!~!!~!!~~~~~!,'''''''''']#@@@@@@@@@@@@@$$1111212122///^}[[[[[<[<<<<k////////////////////@,!,,',!~~~~~~]~''''',*@>@@$@@@(//////////////////,'',,'*~~~~*~''''''@@@@@$@> ", +" 0000000lf;ff;fffffffffgffgggfgfggggggghghghghhdgdhdgdhhdgddhddhdhdcdhddhddddddhdhddhdhdddhddhdddddhdddddddddddddddddddddddddpdjdpdddphpppddpppdpdppdodpdppdppppppdoppdpppppppvppopyvvvyyyvyyyyyyyyyyyyyyyyyyyyyvyyvvvvvyvvvyvyvyvvyvyvyvyyvyvvyyyyyvyyyvyyvyyoa000000l]=**-*-=-==********~@///^666666666n^//////@**~~*~~~~~~~~!~!~~!!~!~~~~,''''''',,,*$@@@@@@>$@@>@>$$111121^/////$}[[[[[[<[<<<^////////////////////-',,,,,~~~~~~~~,'''',,#$@@@@@@@//////////////////','',,~~*~~~~,''',,@@@@@@@> ", +" 000000ae;ffffffffffffgfggffggfggggggggghgghgdhdhdgddhhdhdhdhhhdhdhdhdhddgdhdhddhddhdddhddhddddhddddddddhdddddddddddddddddddddddddpdpppdpdppddpdodpdpddppppppppppppppopppppovovyyyyyyyvvyyyvyvyvyyyvypyvvyvyvvyyyvyyyyyyvyvvyvyvyyvyvyvyvyvyvyvvyvyvyyyyyvyyyyyo000000)**=*=--*-*=*-&***~*&////n66666666666n///////$*~~~~~~~~~~~!~!~~!~!~!!!!!,''''''''',&+@@@$@@@@@@@>@$111^////////2}}[<[<<[<<<k///////(u///////////(~,,','!~~~~~!~~''''''!@@@@$@@@>/////////////////,,,','~~~~~~~''''',@@@@@@@> ", +" 00000acf;f;fe;ffffegffefgfgegggggggghghghggdhhhhddgdhdgdhhddhhdhhddgdhdddhddhddhdddhdcdddddhdddddddddddddddddddddddddddddpdppdddpdddpdpdpddpdpdpdodppppdpppdpppppppppvvovyvyvyvyvvyvyyvyvyvyyyvvyvyyyyyyyyyyyvyvyyyvyyyyyyyyyvyvvvvvvvyvyvyvyyyvvyvvyvyvyvvyyyo000000a===**-*---=**&*9***(///_66666666666666n//////^#**~~~~~~~~~~,~~!~!~~!~~~!,''''''''',,&@@@>$@@@@@@>>@^//////////^2}}}[[[<<<:<_////(tww////////////#',!,,'~~~~~~~~~'''',,&@@@@@@@@/////////////////''',',~~~*~~~,''',,@@@@@@@> ", +" 000000effff;ffffefffffggfgggfgggggggghggdgdgddhdgdhdgddhdgdhdhhdhdhddhdhddhdcdddhdhddddhddddddhddhdcdddhddddddddjddddddpddpdddpddpdpdppddpodppdpddpdodpppppppppppopyyvyyvyvyvyvyyyvyyyyvyyyypyyyyvyvyyyyyyyyyvyyyvyyyyvyyvyyyyyyyyyyvvyvyvyvvvyyvyvzvyyyvyyvyyya000000-**=*=-*-*-***&***$////66666666666666666_//////>&~*~~~~~~~~~~,~~!~!~!!~~~~,''''''',',!#@@@@@@$@@$@@@@(////////^22k}k[<<<<<<<////uwwwx////////////~,,,,,,~~~~~~~~,''''',+@@@@@@@>////////////////,,',,,~*~~~~~''''',@@$>$@+> ", +" 000000aff;fffffffgffgfgfgfgfgggggggghgghgggdhdgdhdhhdhdhdddhhdhdhdhdhddhddgddhhddddhdhddhddhdcddddddddddddddddddddddphoddpdddpddpdpdpddodpdpdpdppodpdppppppppdopyyvvyvyvyyyvyyyyvyvyvyvyyyvyyvyyvyyyyyvyyvyvvyyvyyyyyvyyvyyyvvyvyyvyyyyvyvvyyyyyvyyvyyvyyvyyvyyyj000000e==***-=-=-*&*&***////n6666666666666666665^//////>=*~~~~~~~~~~~~~!~!~~!!~!~~,''''''''''*#>$@@@@@@@@@@@>////////^22kk}k[[<<k_/////xwws////////////+'',,',!~~~~~~~!'''',,*@@@@@@@@(///////////////''''''~~~*~~~,''',,@@@@@@@> ", +" 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<kk555556555555565555655555655555655555655655_//////>&~~~,~!~!~!!!~~~~~~~~~~!~~,'''''''',,*&@@@@@@@@@@@>@@_^///////////////////////////,',,',!~~~~*~~~'''',,*$>$@@@@@$###&%&--*~~~*~*~~~~~~,''',,@@@@@@@> ", +" 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}[[[[[[[[[[[[[[[[[[[[[}[[[[[[[[[[[[[[<<[<[<[<<<<<<<<<<::<////uwwwt/////////nn//////////+*,!',,,,,,,,~*~~~~~~~~~~~~,''''''',,,*$>$@@@@@@@@@@@+$####&%%%&&&-&=!'''',~+@@$@@@@$#$&%%%&=!'',,@@@@@@@> ", +"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[|[||[|||[|||||[||||[|||||[|[||||[|[[[[[[[[[[[[<<[[<[<<<<<k_//////////////(////////////////////>*,,',',',,,!~~~~*~~*~~~~~!,''''''',,,*+@@@@@$>+@@@+@+##$&%#&&#--===!~+@@@@@@@+##&#&-==,,@@$>@@@> ", +" 000000chhhhghhhhhhhhhhhhdhdhhgf;!'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0aaojzjyjjjjjjojjjjyyyzyzj0000008}333|3333338m8m939333||||||||||||||||||||||[|||||||[|[||[|||[[[[[[[[[[[<[<[<[<<<<k^///////////uwx(////////////////////@~',,,,'',,,!~~~~~~~*~~~~~!''''''',,,,&@$>@@@@@@@@@$+$#####&%&%--===&@+@@@@@+$##&%--=~,@@@$@@+> ", +" 000000dhhgchhghghhghhhhhdhdhdgf;'!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''a0aojjjzjyjyzjzjooozyzyyyj000000}83333333333988&333939[|||||||||||||||||||||||||||[||||[||[[|[|[}[[[[[[[[[<[<<<<<<<<k//////////xwww//////////////////////#,',,','',,,~~~*~~~~*~~~~~!'''''',,,,,%@$@@@$>$@@>+@@$###&#&%----*-+>@$>+@@+##&%--==!@@@@@@@> ", +" 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||||||||||||[|[|[[[[[[[<[[[[<<<<<k///////////////////////////////////////%,',,''''''!~~*~~~*~~~*~~~,''''''',,,~)@@+>$@@@@@@@@@###%%&&&@^>>>>>>+#%%&-&>>>$@@@> ", +" 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[[[[[[[[<k^/////////////////////////////////////!,'',',~~~~~~~~~*~*~~~~*~*~~~*~~*~~~,''''''',,,*$@$@@@@@@@@@@+.+>>>>>>>>@+#>^>^>>@> ", +" 000000jdhghhghhhhhhhhhhdhddhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aaajjjjjjjjjjjjjzzyzyyzya0000008939939939393m8mm8999987i7i7i7i7i7i7i7i777iii7i77i78///17777777777|77|73////////////_}[[[<[[<<<k^//////////////////////////////////#','',']~~~~~*~~~~~~~*~~~~~~*~~*~~~~~~!,''''''',',-+@@$>$@@@@@@@@>^>^>^>>>++(>>>>>@> ", +" 000000adhhhchhhhhghhhdddhddhgl;'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0azjjjjjjjjzjjjjjyyzyzz000000b9793999399999m8mmm98999i7i7i7i7i7i7i7i7iii77i7i7i77^////^17777777777|7|^/////////////(_[[[[<[[<<<k/////////////////////////////////,,','',~~~*~~~~~~~~~~~~*~*~~~~~~~*~*~~~~!'''''''',,~&@@@+>$>$@@+@+@>(>>>>^@+(/(^>>>> ", +" 000000ahhhghghghhhhhhdhddhddgf;',''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaajjzjjzjjjjjzajzzyyzyz000000b3939399793998mmm8m999997i7iiii7iii7i7i7i7ii7i7i7ii8///////^377777777772/////////////////2[[[<[<[<<<_//////////////////////////////-''',',~~~~~~~~*~*~*~~~~~~~*~*~*~~~~~*~*~~~,'''''''',,~#@@@)>@@@@@@@@@>(^>>>+^(>((>>^/ ", +" 0000000dhhhhhhhhghhhdchhphdhgf;'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0ajjjjjazjjjjjjjyyzyzyz00000b)3999i89899999m8mmm99999ii7i7i7ii7iiiiiiii7iiiii7i7^/////////$377777777///////////////////^k[[<[<[<<<<^///////////////////////////>',,'',!~~~*~*~~~~~~~~*~~~~~~~~~~~*~*~~~~~~*~~,'''''',,,,*$@@+>$@@@@@@@+>>>(>>(/((^(>(/ ", +" 0000000cdghghhhhhghhdddddddhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',''aaajjjjzajjjjjjzjzyzzyzo000000$79398i9993999mmm8m899997iiiiiiiiiiiii7iiii7i7iiii$////////////$7777771/////////////////////^}[[<[<<<k////////////////////////////]'''','~~~~~~~~~~~~~~~~~*~*~~*~*~~~~~~*~*~~*~~~!,''''''''',-$@@@>$>+@@@@@@>^>(((^(((>(// ", +" 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&,&,,&&!.@<gggzggzzz$lq1111111111111,*>>>>;>;>>>>>>>>>>=*!@+1 ", +" 666ppuusssssssfsfssfsfffffcfcfcfccc7fmfcccmsyyx66w&;,,&,&!&&%5l'ggzggzgk1511qo11111111@==;;>>>>>>>>>>>,>,>*=.+- ", +" 666sspspssssssjfjfffsfff8fffcf7ccfcccc7cfyyyx66t$#%&,,&&&,,>>$~5dggzgz51115ol11111111==>>>;>;>;>>>>>>>>>=*%+@ ", +" 666cxsspssssfsfffjfffffffcffffccc7fmffuyyx666 #$#$!&&&,,=;>;,.1<dgz'~1111l11111111$*=>;>;>;>>>>>>>>>>>*,++- ", +" 666fssssfssfssfffffffff7fccc8ccccfsyyx666m $$#$%!,======;=&@l<g~r111111111111l==>;>;>>>;;;>>>>>>>**#+@ ", +" 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 <vdr04-at-deltab.de> + * based on works from Fabian E. Bustamante <fabianb-at-cs.umd.edu> + * 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 <stdio.h> +#include <stdlib.h> +/* #include <sys/stdtypes.h> for size_t */ +#include <stdarg.h> +#include <errno.h> + +#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<class T> 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 <vdr04-at-deltab.de> + * based on works from Fabian E. Bustamante <fabianb-at-cs.umd.edu> + * 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<class T> 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<class T> 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<class T> 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 <vdr04-at-deltab.de> + * 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <vdr/tools.h> + + +#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(;n<m_nColors;++n) + { + if((m_Colors+n)->color == 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 <vdr04-at-deltab.de> + * 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 <vdr04-at-deltab.de> + * 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 <stdio.h> +#include <string.h> +#include <stdlib.h> + +#ifndef HAVE_FFMPEG_STATIC +#include <dlfcn.h> +#endif + +#include "encode.h" +#include <vdr/device.h> +#include <vdr/tools.h> + +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<m_nFrames && m_nData < m_nMaxMPEGSize; ++i) + { + int nFrameSize = fn_avcodec_encode_video(pAVCC, m_pMPEG + m_nData, m_nMaxMPEGSize - m_nData, pAVF); + if(nFrameSize < 0) + { + esyslog("imageplugin: Failed add %d frame, insufficient memory.",i); + bSuccess = false; + break; + } + bSuccess = true; + m_nData += nFrameSize; + *(m_pFramesSize + i) = nFrameSize; + } + + if(bSuccess && m_nData < m_nMaxMPEGSize) // if sufficient place present + { + //add four bytes end sequnce + memcpy(m_pMPEG + m_nData,"\0x00\0x00\0x01\0xb7",4); + m_nData += 4; + *(m_pFramesSize + i - 1) += 4; + + } + else bSuccess = false; + + #ifdef TESTCODE + if(bSuccess) + Save("/tmp/imagetest.mpg"); + #endif + +encexit: + if (pAVCC) + { + fn_avcodec_close(pAVCC); + free(pAVCC); + } + + if(pAVF) + { + free(pAVF); + } + return bSuccess; +} + + + + +#ifdef TESTCODE +/******************************************************************************* + +*/ +bool cEncode::Load(const char* szFileName) +{ + int nSize = m_nWidth*m_nHeight; + FILE *inf=fopen(szFileName, "r"); + if(inf) + { + fseek(inf, 15, SEEK_SET); + fread(m_pImageRGB, 1, nSize*3, inf); + fclose(inf); + return true; + } + return false; +} + +/******************************************************************************* + +*/ +bool cEncode::Save(const char* szFileName) const +{ + if(m_pMPEG && m_nData) + { + FILE * outf=fopen(szFileName, "w"); + if(outf) { + fwrite(m_pMPEG, 1, m_nData, outf); + fclose(outf); + return true; + } + } + return false; +} +#endif + + +#if 0 +int main(){ + cEncode e; + e.Load("test.pnm"); + e.Encode(); + e.Save("test.mpg"); + return 0; +} +#endif diff --git a/liboutput/encode.h b/liboutput/encode.h new file mode 100644 index 0000000..0491927 --- /dev/null +++ b/liboutput/encode.h @@ -0,0 +1,92 @@ +/*************************************************************************** + * encode.h + * + * (C) Copyright 2004 Andreas Brachold <vdr04-at-deltab.de> + * 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 <libavcodec/avcodec.h> +#else +# include <ffmpeg/avcodec.h> +//#include <libavcodec/avcodec.h> +#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 <vdr04-at-deltab.de> + * 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 <vdr04-at-deltab.de> + * 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 <vdr/thread.h> +#include <vdr/player.h> + +#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 <vdr04-at-deltab.de> + * 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 <string.h> +#include <unistd.h> + +#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 <vdr04-at-deltab.de> + * 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 <vdr/thread.h> +#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 @@ -0,0 +1,221 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 A. Brachold <vdr04-at-deltab.de> + * + * 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 <string.h> + +#include "list.h" +#include "data-image.h" +#include "setup-image.h" + +#include <vdr/tools.h> + +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; +} @@ -0,0 +1,60 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2004 Andreas Brachold <vdr04-at-deltab.de> + * + * 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 <vdr04-at-deltab.de> + * + * 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 <errno.h> +#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 <vdr04-at-deltab.de> + * + * 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 <vdr/menuitems.h> +#include <vdr/interface.h> + +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 <kai@burwieck.net> + * + * 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 <ctype.h> +#include <dirent.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <unistd.h> +#include <typeinfo> + +#include "image.h" +#include "menu.h" +#include "data-image.h" +#include "menu-image.h" +#include "control-image.h" +#include "i18n.h" + +#include <vdr/status.h> + + +// --- 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 <kai@burwieck.net> + * + * 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 <vdr/osdbase.h> +#include <vdr/menuitems.h> + +#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 @@ -0,0 +1,402 @@ +/* + * image plugin to VDR (C++) + * + * (C) 2004 "Interpohl" <interpohl@vdr-portal.de> + * (C) 2004 A.Brachold <vdr04-at-deltab.de> + * (C) 2004 O.Kreuzinger <Onno@Kreuzinger.biz> + * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net> + * (C) 2001,2002 Stefan Huelswitt <huels@iname.com> + * + * 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 <ctype.h> +#include <dirent.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <unistd.h> + +#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; +} @@ -0,0 +1,75 @@ +/* + * Image plugin to VDR (C++) + * + * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net> + * + * based on MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt <huels@iname.com> + * + * 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 <vdr04-at-deltab.de> + * 2003 Kai Tobias Burwieck <kai@burwieck.net> + * + * 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 <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <unistd.h> +#include <signal.h> +#include <wait.h> + +#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 <vdr04-at-deltab.de> + * 2003 Kai Tobias Burwieck <kai@burwieck.net> + * + * 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 <vector> +#define __STL_CONFIG_H + +#include <vdr/thread.h> +#include <vdr/player.h> +#include <liboutput/stillimage-player.h> + +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<cShellWrapper*> +{ + 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()<max_size()) + { + if(NULL == pCmd->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 <vdr04-at-deltab.de> +# base on prior work for convert.sh +# by Onno Kreuzinger <o.kreuzinger-at-kreuzinger.biz> +# Andreas Holzhammer and <Interpohl-at-vdr-portal.de> +# +################################################################################ +# 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 <vdr04-at-deltab.de> + * (C) 2003 Kai Tobias Burwieck <kai-at-burwieck.net> + * + * based on MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt <huels-at-iname.com> + * + * + * 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 <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <vdr/tools.h> +#include <vdr/config.h> +#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 <kai@burwieck.net> + * + * 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 <vdr/menuitems.h> + +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 |